angular-material-wrap 0.1.0-beta.8 → 0.1.0-beta.9
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,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, model, output, ViewEncapsulation, Component, computed, signal, effect, ContentChild, viewChild, Directive, forwardRef, ViewChild, contentChildren,
|
|
2
|
+
import { input, model, output, ViewEncapsulation, Component, computed, signal, effect, ContentChild, viewChild, Directive, Injectable, inject, ElementRef, Renderer2, HostListener, forwardRef, ViewChild, contentChildren, contentChild, Injector, Optional, Inject, Pipe, InjectionToken } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule, NgTemplateOutlet } from '@angular/common';
|
|
5
5
|
import * as i2 from '@angular/material/button';
|
|
@@ -42,7 +42,7 @@ import * as i1$9 from '@angular/cdk/overlay';
|
|
|
42
42
|
import { OverlayModule } from '@angular/cdk/overlay';
|
|
43
43
|
import * as i7 from '@angular/material/menu';
|
|
44
44
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
|
45
|
-
import { Subject, debounceTime, distinctUntilChanged, takeUntil, Subscription, fromEvent, BehaviorSubject, defer, from, of } from 'rxjs';
|
|
45
|
+
import { Subject, debounceTime, distinctUntilChanged, takeUntil, Subscription, fromEvent, Observable, BehaviorSubject, defer, from, of } from 'rxjs';
|
|
46
46
|
import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
|
|
47
47
|
import * as i1$a from '@angular/material/divider';
|
|
48
48
|
import { MatDividerModule } from '@angular/material/divider';
|
|
@@ -3476,6 +3476,610 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
3476
3476
|
}]
|
|
3477
3477
|
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { amwBadge: [{ type: i0.Input, args: [{ isSignal: true, alias: "amwBadge", required: true }] }], amwBadgeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "amwBadgeColor", required: false }] }], amwBadgePosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "amwBadgePosition", required: false }] }], amwBadgeSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "amwBadgeSize", required: false }] }], amwBadgeHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "amwBadgeHidden", required: false }] }], amwBadgeOverlap: [{ type: i0.Input, args: [{ isSignal: true, alias: "amwBadgeOverlap", required: false }] }], amwBadgeClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "amwBadgeClass", required: false }] }] } });
|
|
3478
3478
|
|
|
3479
|
+
const DEFAULT_CONFIG$1 = {
|
|
3480
|
+
autoReevaluate: true,
|
|
3481
|
+
debounceTime: 300,
|
|
3482
|
+
disableOnErrors: true,
|
|
3483
|
+
disableOnWarnings: false
|
|
3484
|
+
};
|
|
3485
|
+
/**
|
|
3486
|
+
* AMW Validation Service
|
|
3487
|
+
*
|
|
3488
|
+
* Provides centralized validation management for components.
|
|
3489
|
+
* Components can create validation contexts, add/remove violations,
|
|
3490
|
+
* and automatically reevaluate when associated controls change.
|
|
3491
|
+
*
|
|
3492
|
+
* @example
|
|
3493
|
+
* ```typescript
|
|
3494
|
+
* // In your component
|
|
3495
|
+
* export class MyFormComponent implements OnInit {
|
|
3496
|
+
* private validationContext!: ValidationContext;
|
|
3497
|
+
*
|
|
3498
|
+
* constructor(private validationService: AmwValidationService) {}
|
|
3499
|
+
*
|
|
3500
|
+
* ngOnInit() {
|
|
3501
|
+
* this.validationContext = this.validationService.createContext({
|
|
3502
|
+
* disableOnErrors: true
|
|
3503
|
+
* });
|
|
3504
|
+
*
|
|
3505
|
+
* // Add a violation
|
|
3506
|
+
* this.validationService.addViolation(this.validationContext.id, {
|
|
3507
|
+
* id: 'name-required',
|
|
3508
|
+
* message: 'Name is required',
|
|
3509
|
+
* severity: 'error',
|
|
3510
|
+
* field: 'name',
|
|
3511
|
+
* control: this.form.get('name'),
|
|
3512
|
+
* validator: () => !!this.form.get('name')?.value
|
|
3513
|
+
* });
|
|
3514
|
+
* }
|
|
3515
|
+
* }
|
|
3516
|
+
* ```
|
|
3517
|
+
*/
|
|
3518
|
+
class AmwValidationService {
|
|
3519
|
+
/** Map of all validation contexts by ID */
|
|
3520
|
+
contexts = new Map();
|
|
3521
|
+
/** Counter for generating unique IDs */
|
|
3522
|
+
idCounter = 0;
|
|
3523
|
+
/** Map of control subscriptions for cleanup */
|
|
3524
|
+
controlSubscriptions = new Map();
|
|
3525
|
+
constructor() { }
|
|
3526
|
+
/**
|
|
3527
|
+
* Create a new validation context for a component
|
|
3528
|
+
* @param config Optional configuration
|
|
3529
|
+
* @returns The created validation context
|
|
3530
|
+
*/
|
|
3531
|
+
createContext(config) {
|
|
3532
|
+
const id = this.generateId();
|
|
3533
|
+
const violations = signal([], ...(ngDevMode ? [{ debugName: "violations" }] : []));
|
|
3534
|
+
const mergedConfig = { ...DEFAULT_CONFIG$1, ...config };
|
|
3535
|
+
const hasErrors = computed(() => violations().some(v => v.severity === 'error'), ...(ngDevMode ? [{ debugName: "hasErrors" }] : []));
|
|
3536
|
+
const hasWarnings = computed(() => violations().some(v => v.severity === 'warning'), ...(ngDevMode ? [{ debugName: "hasWarnings" }] : []));
|
|
3537
|
+
const isSubmitDisabled = computed(() => {
|
|
3538
|
+
if (mergedConfig.disableOnErrors && hasErrors())
|
|
3539
|
+
return true;
|
|
3540
|
+
if (mergedConfig.disableOnWarnings && hasWarnings())
|
|
3541
|
+
return true;
|
|
3542
|
+
return false;
|
|
3543
|
+
}, ...(ngDevMode ? [{ debugName: "isSubmitDisabled" }] : []));
|
|
3544
|
+
const context = {
|
|
3545
|
+
id,
|
|
3546
|
+
violations,
|
|
3547
|
+
hasErrors,
|
|
3548
|
+
hasWarnings,
|
|
3549
|
+
isSubmitDisabled,
|
|
3550
|
+
config: mergedConfig
|
|
3551
|
+
};
|
|
3552
|
+
this.contexts.set(id, context);
|
|
3553
|
+
return context;
|
|
3554
|
+
}
|
|
3555
|
+
/**
|
|
3556
|
+
* Destroy a validation context and clean up subscriptions
|
|
3557
|
+
* @param contextId The context ID to destroy
|
|
3558
|
+
*/
|
|
3559
|
+
destroyContext(contextId) {
|
|
3560
|
+
// Clean up control subscriptions
|
|
3561
|
+
const subscriptions = this.controlSubscriptions.get(contextId);
|
|
3562
|
+
if (subscriptions) {
|
|
3563
|
+
subscriptions.forEach(unsub => unsub());
|
|
3564
|
+
this.controlSubscriptions.delete(contextId);
|
|
3565
|
+
}
|
|
3566
|
+
this.contexts.delete(contextId);
|
|
3567
|
+
}
|
|
3568
|
+
/**
|
|
3569
|
+
* Add a violation to a context
|
|
3570
|
+
* @param contextId The context ID
|
|
3571
|
+
* @param violation The violation to add (without ID, one will be generated)
|
|
3572
|
+
* @returns The violation ID
|
|
3573
|
+
*/
|
|
3574
|
+
addViolation(contextId, violation) {
|
|
3575
|
+
const context = this.contexts.get(contextId);
|
|
3576
|
+
if (!context) {
|
|
3577
|
+
throw new Error(`Validation context ${contextId} not found`);
|
|
3578
|
+
}
|
|
3579
|
+
const violationId = violation.id || this.generateViolationId();
|
|
3580
|
+
const fullViolation = {
|
|
3581
|
+
...violation,
|
|
3582
|
+
id: violationId
|
|
3583
|
+
};
|
|
3584
|
+
// Check if violation with same ID already exists
|
|
3585
|
+
const existing = context.violations().find(v => v.id === violationId);
|
|
3586
|
+
if (existing) {
|
|
3587
|
+
// Update existing violation
|
|
3588
|
+
context.violations.update(violations => violations.map(v => v.id === violationId ? fullViolation : v));
|
|
3589
|
+
}
|
|
3590
|
+
else {
|
|
3591
|
+
// Add new violation
|
|
3592
|
+
context.violations.update(violations => [...violations, fullViolation]);
|
|
3593
|
+
}
|
|
3594
|
+
// Set up control subscription for auto-reevaluation
|
|
3595
|
+
if (context.config.autoReevaluate && fullViolation.control) {
|
|
3596
|
+
this.setupControlSubscription(contextId, fullViolation);
|
|
3597
|
+
}
|
|
3598
|
+
return violationId;
|
|
3599
|
+
}
|
|
3600
|
+
/**
|
|
3601
|
+
* Remove a violation from a context
|
|
3602
|
+
* @param contextId The context ID
|
|
3603
|
+
* @param violationId The violation ID to remove
|
|
3604
|
+
*/
|
|
3605
|
+
removeViolation(contextId, violationId) {
|
|
3606
|
+
const context = this.contexts.get(contextId);
|
|
3607
|
+
if (!context)
|
|
3608
|
+
return;
|
|
3609
|
+
context.violations.update(violations => violations.filter(v => v.id !== violationId));
|
|
3610
|
+
}
|
|
3611
|
+
/**
|
|
3612
|
+
* Clear all violations from a context
|
|
3613
|
+
* @param contextId The context ID
|
|
3614
|
+
*/
|
|
3615
|
+
clearViolations(contextId) {
|
|
3616
|
+
const context = this.contexts.get(contextId);
|
|
3617
|
+
if (!context)
|
|
3618
|
+
return;
|
|
3619
|
+
context.violations.set([]);
|
|
3620
|
+
}
|
|
3621
|
+
/**
|
|
3622
|
+
* Get all violations for a context
|
|
3623
|
+
* @param contextId The context ID
|
|
3624
|
+
* @returns Array of violations
|
|
3625
|
+
*/
|
|
3626
|
+
getViolations(contextId) {
|
|
3627
|
+
const context = this.contexts.get(contextId);
|
|
3628
|
+
return context?.violations() || [];
|
|
3629
|
+
}
|
|
3630
|
+
/**
|
|
3631
|
+
* Get violations for a specific field
|
|
3632
|
+
* @param contextId The context ID
|
|
3633
|
+
* @param field The field name
|
|
3634
|
+
* @returns Array of violations for the field
|
|
3635
|
+
*/
|
|
3636
|
+
getFieldViolations(contextId, field) {
|
|
3637
|
+
return this.getViolations(contextId).filter(v => v.field === field);
|
|
3638
|
+
}
|
|
3639
|
+
/**
|
|
3640
|
+
* Check if a context has any violations
|
|
3641
|
+
* @param contextId The context ID
|
|
3642
|
+
* @returns True if violations exist
|
|
3643
|
+
*/
|
|
3644
|
+
hasViolations(contextId) {
|
|
3645
|
+
return this.getViolations(contextId).length > 0;
|
|
3646
|
+
}
|
|
3647
|
+
/**
|
|
3648
|
+
* Reevaluate all violations in a context
|
|
3649
|
+
* @param contextId The context ID
|
|
3650
|
+
*/
|
|
3651
|
+
reevaluate(contextId) {
|
|
3652
|
+
const context = this.contexts.get(contextId);
|
|
3653
|
+
if (!context)
|
|
3654
|
+
return;
|
|
3655
|
+
context.violations.update(violations => violations.filter(v => {
|
|
3656
|
+
if (v.validator) {
|
|
3657
|
+
return !v.validator(); // Keep violation if validator returns false
|
|
3658
|
+
}
|
|
3659
|
+
if (v.control) {
|
|
3660
|
+
return v.control.invalid; // Keep violation if control is invalid
|
|
3661
|
+
}
|
|
3662
|
+
return true; // Keep violations without validator or control
|
|
3663
|
+
}));
|
|
3664
|
+
}
|
|
3665
|
+
/**
|
|
3666
|
+
* Get a context by ID
|
|
3667
|
+
* @param contextId The context ID
|
|
3668
|
+
* @returns The validation context or undefined
|
|
3669
|
+
*/
|
|
3670
|
+
getContext(contextId) {
|
|
3671
|
+
return this.contexts.get(contextId);
|
|
3672
|
+
}
|
|
3673
|
+
/**
|
|
3674
|
+
* Set up a subscription to a control for auto-reevaluation
|
|
3675
|
+
*/
|
|
3676
|
+
setupControlSubscription(contextId, violation) {
|
|
3677
|
+
if (!violation.control)
|
|
3678
|
+
return;
|
|
3679
|
+
let debounceTimer = null;
|
|
3680
|
+
const context = this.contexts.get(contextId);
|
|
3681
|
+
if (!context)
|
|
3682
|
+
return;
|
|
3683
|
+
const subscription = violation.control.valueChanges.subscribe(() => {
|
|
3684
|
+
if (debounceTimer) {
|
|
3685
|
+
clearTimeout(debounceTimer);
|
|
3686
|
+
}
|
|
3687
|
+
debounceTimer = setTimeout(() => {
|
|
3688
|
+
// Reevaluate this specific violation
|
|
3689
|
+
const shouldKeep = violation.validator
|
|
3690
|
+
? !violation.validator()
|
|
3691
|
+
: violation.control?.invalid;
|
|
3692
|
+
if (!shouldKeep) {
|
|
3693
|
+
this.removeViolation(contextId, violation.id);
|
|
3694
|
+
}
|
|
3695
|
+
}, context.config.debounceTime);
|
|
3696
|
+
});
|
|
3697
|
+
// Store unsubscribe function
|
|
3698
|
+
const subscriptions = this.controlSubscriptions.get(contextId) || [];
|
|
3699
|
+
subscriptions.push(() => subscription.unsubscribe());
|
|
3700
|
+
this.controlSubscriptions.set(contextId, subscriptions);
|
|
3701
|
+
}
|
|
3702
|
+
generateId() {
|
|
3703
|
+
return `vc_${Date.now()}_${++this.idCounter}`;
|
|
3704
|
+
}
|
|
3705
|
+
generateViolationId() {
|
|
3706
|
+
return `vv_${Date.now()}_${++this.idCounter}`;
|
|
3707
|
+
}
|
|
3708
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwValidationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3709
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwValidationService, providedIn: 'root' });
|
|
3710
|
+
}
|
|
3711
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwValidationService, decorators: [{
|
|
3712
|
+
type: Injectable,
|
|
3713
|
+
args: [{
|
|
3714
|
+
providedIn: 'root'
|
|
3715
|
+
}]
|
|
3716
|
+
}], ctorParameters: () => [] });
|
|
3717
|
+
/**
|
|
3718
|
+
* Mixin/base class functionality for components that need validation
|
|
3719
|
+
* This can be used via composition or inheritance
|
|
3720
|
+
*/
|
|
3721
|
+
class ValidationMixin {
|
|
3722
|
+
validationService;
|
|
3723
|
+
validationContext;
|
|
3724
|
+
/**
|
|
3725
|
+
* Initialize validation for this component
|
|
3726
|
+
* Call this in ngOnInit
|
|
3727
|
+
*/
|
|
3728
|
+
initializeValidation(service, config) {
|
|
3729
|
+
this.validationService = service;
|
|
3730
|
+
this.validationContext = service.createContext(config);
|
|
3731
|
+
}
|
|
3732
|
+
/**
|
|
3733
|
+
* Clean up validation context
|
|
3734
|
+
* Call this in ngOnDestroy
|
|
3735
|
+
*/
|
|
3736
|
+
destroyValidation() {
|
|
3737
|
+
if (this.validationContext) {
|
|
3738
|
+
this.validationService.destroyContext(this.validationContext.id);
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
/**
|
|
3742
|
+
* Add a validation violation
|
|
3743
|
+
*/
|
|
3744
|
+
addViolation(violation) {
|
|
3745
|
+
return this.validationService.addViolation(this.validationContext.id, violation);
|
|
3746
|
+
}
|
|
3747
|
+
/**
|
|
3748
|
+
* Remove a validation violation
|
|
3749
|
+
*/
|
|
3750
|
+
removeViolation(violationId) {
|
|
3751
|
+
this.validationService.removeViolation(this.validationContext.id, violationId);
|
|
3752
|
+
}
|
|
3753
|
+
/**
|
|
3754
|
+
* Clear all violations
|
|
3755
|
+
*/
|
|
3756
|
+
clearViolations() {
|
|
3757
|
+
this.validationService.clearViolations(this.validationContext.id);
|
|
3758
|
+
}
|
|
3759
|
+
/**
|
|
3760
|
+
* Get all violations
|
|
3761
|
+
*/
|
|
3762
|
+
get violations() {
|
|
3763
|
+
return this.validationContext?.violations() || [];
|
|
3764
|
+
}
|
|
3765
|
+
/**
|
|
3766
|
+
* Check if submit should be disabled
|
|
3767
|
+
*/
|
|
3768
|
+
get isSubmitDisabled() {
|
|
3769
|
+
return this.validationContext?.isSubmitDisabled() || false;
|
|
3770
|
+
}
|
|
3771
|
+
/**
|
|
3772
|
+
* Check if there are any errors
|
|
3773
|
+
*/
|
|
3774
|
+
get hasErrors() {
|
|
3775
|
+
return this.validationContext?.hasErrors() || false;
|
|
3776
|
+
}
|
|
3777
|
+
/**
|
|
3778
|
+
* Check if there are any warnings
|
|
3779
|
+
*/
|
|
3780
|
+
get hasWarnings() {
|
|
3781
|
+
return this.validationContext?.hasWarnings() || false;
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
|
|
3785
|
+
/**
|
|
3786
|
+
* AMW Validation Tooltip Directive
|
|
3787
|
+
*
|
|
3788
|
+
* Displays validation violations in a tooltip when hovering over a submit button.
|
|
3789
|
+
* Works with the AmwValidationService to show relevant validation messages.
|
|
3790
|
+
*
|
|
3791
|
+
* When violations exist and the user hovers over the button, a tooltip appears
|
|
3792
|
+
* showing all current validation messages. The button is also automatically
|
|
3793
|
+
* disabled based on the validation context configuration.
|
|
3794
|
+
*
|
|
3795
|
+
* @example
|
|
3796
|
+
* ```html
|
|
3797
|
+
* <!-- Basic usage with validation context ID -->
|
|
3798
|
+
* <button [amwValidationTooltip]="validationContext.id">
|
|
3799
|
+
* Submit
|
|
3800
|
+
* </button>
|
|
3801
|
+
*
|
|
3802
|
+
* <!-- With validation context object -->
|
|
3803
|
+
* <button [amwValidationTooltip]="validationContext">
|
|
3804
|
+
* Submit
|
|
3805
|
+
* </button>
|
|
3806
|
+
*
|
|
3807
|
+
* <!-- Auto-disable based on validation state -->
|
|
3808
|
+
* <amw-button
|
|
3809
|
+
* [amwValidationTooltip]="validationContext"
|
|
3810
|
+
* [disabled]="validationContext.isSubmitDisabled()">
|
|
3811
|
+
* Submit
|
|
3812
|
+
* </amw-button>
|
|
3813
|
+
* ```
|
|
3814
|
+
*/
|
|
3815
|
+
class AmwValidationTooltipDirective {
|
|
3816
|
+
elementRef = inject(ElementRef);
|
|
3817
|
+
renderer = inject(Renderer2);
|
|
3818
|
+
validationService = inject(AmwValidationService);
|
|
3819
|
+
/**
|
|
3820
|
+
* The validation context (ID string or context object)
|
|
3821
|
+
*/
|
|
3822
|
+
amwValidationTooltip = input.required(...(ngDevMode ? [{ debugName: "amwValidationTooltip" }] : []));
|
|
3823
|
+
/**
|
|
3824
|
+
* Position of the tooltip
|
|
3825
|
+
*/
|
|
3826
|
+
tooltipPosition = input('top', ...(ngDevMode ? [{ debugName: "tooltipPosition" }] : []));
|
|
3827
|
+
/**
|
|
3828
|
+
* Whether to auto-disable the element when validation fails
|
|
3829
|
+
*/
|
|
3830
|
+
autoDisable = input(true, ...(ngDevMode ? [{ debugName: "autoDisable" }] : []));
|
|
3831
|
+
/** The tooltip element */
|
|
3832
|
+
tooltipElement = null;
|
|
3833
|
+
/** Whether tooltip is currently visible */
|
|
3834
|
+
isTooltipVisible = false;
|
|
3835
|
+
/** Timeout for delayed show */
|
|
3836
|
+
showTimeout = null;
|
|
3837
|
+
/** Timeout for delayed hide */
|
|
3838
|
+
hideTimeout = null;
|
|
3839
|
+
constructor() {
|
|
3840
|
+
// Set up effect to auto-disable based on validation state
|
|
3841
|
+
effect(() => {
|
|
3842
|
+
if (this.autoDisable()) {
|
|
3843
|
+
const context = this.getContext();
|
|
3844
|
+
if (context && context.isSubmitDisabled()) {
|
|
3845
|
+
this.renderer.setAttribute(this.elementRef.nativeElement, 'disabled', 'true');
|
|
3846
|
+
}
|
|
3847
|
+
else {
|
|
3848
|
+
this.renderer.removeAttribute(this.elementRef.nativeElement, 'disabled');
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
});
|
|
3852
|
+
}
|
|
3853
|
+
ngOnInit() {
|
|
3854
|
+
this.createTooltipElement();
|
|
3855
|
+
}
|
|
3856
|
+
ngOnDestroy() {
|
|
3857
|
+
this.removeTooltip();
|
|
3858
|
+
if (this.showTimeout)
|
|
3859
|
+
clearTimeout(this.showTimeout);
|
|
3860
|
+
if (this.hideTimeout)
|
|
3861
|
+
clearTimeout(this.hideTimeout);
|
|
3862
|
+
}
|
|
3863
|
+
onMouseEnter() {
|
|
3864
|
+
const violations = this.getViolations();
|
|
3865
|
+
if (violations.length > 0) {
|
|
3866
|
+
if (this.hideTimeout) {
|
|
3867
|
+
clearTimeout(this.hideTimeout);
|
|
3868
|
+
this.hideTimeout = null;
|
|
3869
|
+
}
|
|
3870
|
+
this.showTimeout = setTimeout(() => this.showTooltip(), 200);
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
onMouseLeave() {
|
|
3874
|
+
if (this.showTimeout) {
|
|
3875
|
+
clearTimeout(this.showTimeout);
|
|
3876
|
+
this.showTimeout = null;
|
|
3877
|
+
}
|
|
3878
|
+
this.hideTimeout = setTimeout(() => this.hideTooltip(), 100);
|
|
3879
|
+
}
|
|
3880
|
+
onFocus() {
|
|
3881
|
+
const violations = this.getViolations();
|
|
3882
|
+
if (violations.length > 0) {
|
|
3883
|
+
this.showTooltip();
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
onBlur() {
|
|
3887
|
+
this.hideTooltip();
|
|
3888
|
+
}
|
|
3889
|
+
/**
|
|
3890
|
+
* Get the validation context
|
|
3891
|
+
*/
|
|
3892
|
+
getContext() {
|
|
3893
|
+
const input = this.amwValidationTooltip();
|
|
3894
|
+
if (typeof input === 'string') {
|
|
3895
|
+
return this.validationService.getContext(input);
|
|
3896
|
+
}
|
|
3897
|
+
return input;
|
|
3898
|
+
}
|
|
3899
|
+
/**
|
|
3900
|
+
* Get current violations
|
|
3901
|
+
*/
|
|
3902
|
+
getViolations() {
|
|
3903
|
+
const context = this.getContext();
|
|
3904
|
+
return context?.violations() || [];
|
|
3905
|
+
}
|
|
3906
|
+
/**
|
|
3907
|
+
* Create the tooltip element
|
|
3908
|
+
*/
|
|
3909
|
+
createTooltipElement() {
|
|
3910
|
+
this.tooltipElement = this.renderer.createElement('div');
|
|
3911
|
+
this.renderer.addClass(this.tooltipElement, 'amw-validation-tooltip');
|
|
3912
|
+
this.renderer.setStyle(this.tooltipElement, 'position', 'fixed');
|
|
3913
|
+
this.renderer.setStyle(this.tooltipElement, 'z-index', '10000');
|
|
3914
|
+
this.renderer.setStyle(this.tooltipElement, 'pointer-events', 'none');
|
|
3915
|
+
this.renderer.setStyle(this.tooltipElement, 'opacity', '0');
|
|
3916
|
+
this.renderer.setStyle(this.tooltipElement, 'visibility', 'hidden');
|
|
3917
|
+
this.renderer.setStyle(this.tooltipElement, 'transition', 'opacity 200ms, visibility 200ms');
|
|
3918
|
+
// Use softer info-style colors for less jarring appearance
|
|
3919
|
+
this.renderer.setStyle(this.tooltipElement, 'background-color', '#e3f2fd');
|
|
3920
|
+
this.renderer.setStyle(this.tooltipElement, 'color', '#002171');
|
|
3921
|
+
this.renderer.setStyle(this.tooltipElement, 'padding', '8px 12px');
|
|
3922
|
+
this.renderer.setStyle(this.tooltipElement, 'border-radius', '4px');
|
|
3923
|
+
this.renderer.setStyle(this.tooltipElement, 'font-size', '12px');
|
|
3924
|
+
this.renderer.setStyle(this.tooltipElement, 'max-width', '300px');
|
|
3925
|
+
this.renderer.setStyle(this.tooltipElement, 'box-shadow', '0 2px 8px rgba(0,0,0,0.15)');
|
|
3926
|
+
if (this.tooltipElement) {
|
|
3927
|
+
document.body.appendChild(this.tooltipElement);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
/**
|
|
3931
|
+
* Show the tooltip
|
|
3932
|
+
*/
|
|
3933
|
+
showTooltip() {
|
|
3934
|
+
if (!this.tooltipElement)
|
|
3935
|
+
return;
|
|
3936
|
+
const violations = this.getViolations();
|
|
3937
|
+
if (violations.length === 0)
|
|
3938
|
+
return;
|
|
3939
|
+
// Build tooltip content
|
|
3940
|
+
const content = this.buildTooltipContent(violations);
|
|
3941
|
+
this.tooltipElement.innerHTML = content;
|
|
3942
|
+
// Position the tooltip
|
|
3943
|
+
this.positionTooltip();
|
|
3944
|
+
// Show with animation
|
|
3945
|
+
this.renderer.setStyle(this.tooltipElement, 'opacity', '1');
|
|
3946
|
+
this.renderer.setStyle(this.tooltipElement, 'visibility', 'visible');
|
|
3947
|
+
this.isTooltipVisible = true;
|
|
3948
|
+
}
|
|
3949
|
+
/**
|
|
3950
|
+
* Hide the tooltip
|
|
3951
|
+
*/
|
|
3952
|
+
hideTooltip() {
|
|
3953
|
+
if (!this.tooltipElement)
|
|
3954
|
+
return;
|
|
3955
|
+
this.renderer.setStyle(this.tooltipElement, 'opacity', '0');
|
|
3956
|
+
this.renderer.setStyle(this.tooltipElement, 'visibility', 'hidden');
|
|
3957
|
+
this.isTooltipVisible = false;
|
|
3958
|
+
}
|
|
3959
|
+
/**
|
|
3960
|
+
* Build tooltip HTML content
|
|
3961
|
+
*/
|
|
3962
|
+
buildTooltipContent(violations) {
|
|
3963
|
+
const errors = violations.filter(v => v.severity === 'error');
|
|
3964
|
+
const warnings = violations.filter(v => v.severity === 'warning');
|
|
3965
|
+
const infos = violations.filter(v => v.severity === 'info');
|
|
3966
|
+
let html = '<div class="amw-validation-tooltip__content">';
|
|
3967
|
+
if (errors.length > 0) {
|
|
3968
|
+
html += '<div class="amw-validation-tooltip__section amw-validation-tooltip__section--error">';
|
|
3969
|
+
html += '<strong>Errors:</strong>';
|
|
3970
|
+
html += '<ul style="margin: 4px 0 0 0; padding-left: 16px;">';
|
|
3971
|
+
errors.forEach(e => {
|
|
3972
|
+
html += `<li>${this.escapeHtml(e.message)}</li>`;
|
|
3973
|
+
});
|
|
3974
|
+
html += '</ul></div>';
|
|
3975
|
+
}
|
|
3976
|
+
if (warnings.length > 0) {
|
|
3977
|
+
html += '<div class="amw-validation-tooltip__section amw-validation-tooltip__section--warning" style="margin-top: 8px;">';
|
|
3978
|
+
html += '<strong>Warnings:</strong>';
|
|
3979
|
+
html += '<ul style="margin: 4px 0 0 0; padding-left: 16px;">';
|
|
3980
|
+
warnings.forEach(w => {
|
|
3981
|
+
html += `<li>${this.escapeHtml(w.message)}</li>`;
|
|
3982
|
+
});
|
|
3983
|
+
html += '</ul></div>';
|
|
3984
|
+
}
|
|
3985
|
+
if (infos.length > 0) {
|
|
3986
|
+
html += '<div class="amw-validation-tooltip__section amw-validation-tooltip__section--info" style="margin-top: 8px;">';
|
|
3987
|
+
html += '<strong>Info:</strong>';
|
|
3988
|
+
html += '<ul style="margin: 4px 0 0 0; padding-left: 16px;">';
|
|
3989
|
+
infos.forEach(i => {
|
|
3990
|
+
html += `<li>${this.escapeHtml(i.message)}</li>`;
|
|
3991
|
+
});
|
|
3992
|
+
html += '</ul></div>';
|
|
3993
|
+
}
|
|
3994
|
+
html += '</div>';
|
|
3995
|
+
return html;
|
|
3996
|
+
}
|
|
3997
|
+
/**
|
|
3998
|
+
* Position the tooltip relative to the host element
|
|
3999
|
+
*/
|
|
4000
|
+
positionTooltip() {
|
|
4001
|
+
if (!this.tooltipElement)
|
|
4002
|
+
return;
|
|
4003
|
+
const hostRect = this.elementRef.nativeElement.getBoundingClientRect();
|
|
4004
|
+
const tooltipRect = this.tooltipElement.getBoundingClientRect();
|
|
4005
|
+
const position = this.tooltipPosition();
|
|
4006
|
+
const offset = 8;
|
|
4007
|
+
let top;
|
|
4008
|
+
let left;
|
|
4009
|
+
switch (position) {
|
|
4010
|
+
case 'top':
|
|
4011
|
+
top = hostRect.top - tooltipRect.height - offset;
|
|
4012
|
+
left = hostRect.left + (hostRect.width - tooltipRect.width) / 2;
|
|
4013
|
+
break;
|
|
4014
|
+
case 'bottom':
|
|
4015
|
+
top = hostRect.bottom + offset;
|
|
4016
|
+
left = hostRect.left + (hostRect.width - tooltipRect.width) / 2;
|
|
4017
|
+
break;
|
|
4018
|
+
case 'left':
|
|
4019
|
+
top = hostRect.top + (hostRect.height - tooltipRect.height) / 2;
|
|
4020
|
+
left = hostRect.left - tooltipRect.width - offset;
|
|
4021
|
+
break;
|
|
4022
|
+
case 'right':
|
|
4023
|
+
top = hostRect.top + (hostRect.height - tooltipRect.height) / 2;
|
|
4024
|
+
left = hostRect.right + offset;
|
|
4025
|
+
break;
|
|
4026
|
+
}
|
|
4027
|
+
// Keep tooltip within viewport
|
|
4028
|
+
const viewportWidth = window.innerWidth;
|
|
4029
|
+
const viewportHeight = window.innerHeight;
|
|
4030
|
+
if (left < 8)
|
|
4031
|
+
left = 8;
|
|
4032
|
+
if (left + tooltipRect.width > viewportWidth - 8) {
|
|
4033
|
+
left = viewportWidth - tooltipRect.width - 8;
|
|
4034
|
+
}
|
|
4035
|
+
if (top < 8)
|
|
4036
|
+
top = 8;
|
|
4037
|
+
if (top + tooltipRect.height > viewportHeight - 8) {
|
|
4038
|
+
top = viewportHeight - tooltipRect.height - 8;
|
|
4039
|
+
}
|
|
4040
|
+
this.renderer.setStyle(this.tooltipElement, 'top', `${top}px`);
|
|
4041
|
+
this.renderer.setStyle(this.tooltipElement, 'left', `${left}px`);
|
|
4042
|
+
}
|
|
4043
|
+
/**
|
|
4044
|
+
* Remove tooltip element from DOM
|
|
4045
|
+
*/
|
|
4046
|
+
removeTooltip() {
|
|
4047
|
+
if (this.tooltipElement && this.tooltipElement.parentNode) {
|
|
4048
|
+
this.tooltipElement.parentNode.removeChild(this.tooltipElement);
|
|
4049
|
+
this.tooltipElement = null;
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
/**
|
|
4053
|
+
* Escape HTML to prevent XSS
|
|
4054
|
+
*/
|
|
4055
|
+
escapeHtml(text) {
|
|
4056
|
+
const div = document.createElement('div');
|
|
4057
|
+
div.textContent = text;
|
|
4058
|
+
return div.innerHTML;
|
|
4059
|
+
}
|
|
4060
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwValidationTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4061
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: AmwValidationTooltipDirective, isStandalone: true, selector: "[amwValidationTooltip]", inputs: { amwValidationTooltip: { classPropertyName: "amwValidationTooltip", publicName: "amwValidationTooltip", isSignal: true, isRequired: true, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, autoDisable: { classPropertyName: "autoDisable", publicName: "autoDisable", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "focus": "onFocus()", "blur": "onBlur()" } }, ngImport: i0 });
|
|
4062
|
+
}
|
|
4063
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwValidationTooltipDirective, decorators: [{
|
|
4064
|
+
type: Directive,
|
|
4065
|
+
args: [{
|
|
4066
|
+
selector: '[amwValidationTooltip]',
|
|
4067
|
+
standalone: true
|
|
4068
|
+
}]
|
|
4069
|
+
}], ctorParameters: () => [], propDecorators: { amwValidationTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "amwValidationTooltip", required: true }] }], tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: false }] }], autoDisable: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoDisable", required: false }] }], onMouseEnter: [{
|
|
4070
|
+
type: HostListener,
|
|
4071
|
+
args: ['mouseenter']
|
|
4072
|
+
}], onMouseLeave: [{
|
|
4073
|
+
type: HostListener,
|
|
4074
|
+
args: ['mouseleave']
|
|
4075
|
+
}], onFocus: [{
|
|
4076
|
+
type: HostListener,
|
|
4077
|
+
args: ['focus']
|
|
4078
|
+
}], onBlur: [{
|
|
4079
|
+
type: HostListener,
|
|
4080
|
+
args: ['blur']
|
|
4081
|
+
}] } });
|
|
4082
|
+
|
|
3479
4083
|
/**
|
|
3480
4084
|
* Angular Material Wrap Divider Component
|
|
3481
4085
|
*
|
|
@@ -12177,6 +12781,891 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
12177
12781
|
], encapsulation: ViewEncapsulation.None, template: "<mat-paginator\n [class]=\"paginatorClasses()\"\n [length]=\"length()\"\n [pageSize]=\"pageSize()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n [pageIndex]=\"pageIndex()\"\n [showFirstLastButtons]=\"showFirstLastButtons()\"\n [hidePageSize]=\"hidePageSize()\"\n [disabled]=\"disabled()\"\n (page)=\"onPageChange($event)\"\n aria-label=\"Select page\">\n</mat-paginator>\n", styles: [".amw-paginator{display:flex;align-items:center}.amw-paginator--disabled{opacity:.5;pointer-events:none}.amw-paginator--hide-page-size .mat-mdc-paginator-page-size{display:none}.amw-paginator .mat-mdc-paginator-container{padding:0 8px;min-height:56px}.amw-paginator .mat-mdc-paginator-page-size-label{margin:0 4px}.amw-paginator .mat-mdc-paginator-range-label{margin:0 16px}.amw-paginator .mat-mdc-paginator-navigation-first:disabled,.amw-paginator .mat-mdc-paginator-navigation-previous:disabled,.amw-paginator .mat-mdc-paginator-navigation-next:disabled,.amw-paginator .mat-mdc-paginator-navigation-last:disabled{opacity:.38}\n"] }]
|
|
12178
12782
|
}], propDecorators: { length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], pageIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageIndex", required: false }] }, { type: i0.Output, args: ["pageIndexChange"] }], showFirstLastButtons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFirstLastButtons", required: false }] }], hidePageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidePageSize", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], paginatorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginatorClass", required: false }] }], page: [{ type: i0.Output, args: ["page"] }] } });
|
|
12179
12783
|
|
|
12784
|
+
/**
|
|
12785
|
+
* Full Screen Loading Service
|
|
12786
|
+
*
|
|
12787
|
+
* Manages a queue of loading messages that can be driven by observables.
|
|
12788
|
+
* Each observable can add its own message, and when the observable completes
|
|
12789
|
+
* or emits a value, the message is dismissed with animation.
|
|
12790
|
+
*
|
|
12791
|
+
* @example
|
|
12792
|
+
* ```typescript
|
|
12793
|
+
* // Using the loading() operator
|
|
12794
|
+
* this.userService.getUser(id)
|
|
12795
|
+
* .pipe(loading('Loading user...'))
|
|
12796
|
+
* .subscribe(user => this.user = user);
|
|
12797
|
+
*
|
|
12798
|
+
* // Multiple simultaneous operations
|
|
12799
|
+
* forkJoin([
|
|
12800
|
+
* this.service1.getData().pipe(loading('Loading data 1...')),
|
|
12801
|
+
* this.service2.getData().pipe(loading('Loading data 2...'))
|
|
12802
|
+
* ]).subscribe(results => this.results = results);
|
|
12803
|
+
* ```
|
|
12804
|
+
*/
|
|
12805
|
+
class AmwFullScreenLoadingService {
|
|
12806
|
+
/** Internal signal holding all loading items */
|
|
12807
|
+
loadingItemsSignal = signal([], ...(ngDevMode ? [{ debugName: "loadingItemsSignal" }] : []));
|
|
12808
|
+
/** Signal indicating whether the overlay is visible */
|
|
12809
|
+
overlayVisibleSignal = signal(false, ...(ngDevMode ? [{ debugName: "overlayVisibleSignal" }] : []));
|
|
12810
|
+
/** Timeout for overlay dismiss animation */
|
|
12811
|
+
overlayDismissTimeout = null;
|
|
12812
|
+
/** Animation duration for items dismissing (ms) */
|
|
12813
|
+
itemDismissAnimationDuration = 300;
|
|
12814
|
+
/** Animation duration for overlay appearing/disappearing (ms) */
|
|
12815
|
+
overlayAnimationDuration = 300;
|
|
12816
|
+
/** Read-only computed signal of loading items */
|
|
12817
|
+
loadingItems = computed(() => this.loadingItemsSignal(), ...(ngDevMode ? [{ debugName: "loadingItems" }] : []));
|
|
12818
|
+
/** Read-only computed signal of whether any loading is active */
|
|
12819
|
+
isLoading = computed(() => this.loadingItemsSignal().length > 0, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
12820
|
+
/** Read-only signal of whether overlay is visible (for animation timing) */
|
|
12821
|
+
overlayVisible = computed(() => this.overlayVisibleSignal(), ...(ngDevMode ? [{ debugName: "overlayVisible" }] : []));
|
|
12822
|
+
/** Counter for generating unique IDs */
|
|
12823
|
+
idCounter = 0;
|
|
12824
|
+
constructor() { }
|
|
12825
|
+
/**
|
|
12826
|
+
* Add a loading item to the queue
|
|
12827
|
+
* @param message The message to display
|
|
12828
|
+
* @returns The unique ID of the loading item
|
|
12829
|
+
*/
|
|
12830
|
+
add(message) {
|
|
12831
|
+
const id = this.generateId();
|
|
12832
|
+
const item = {
|
|
12833
|
+
id,
|
|
12834
|
+
message,
|
|
12835
|
+
dismissing: false,
|
|
12836
|
+
timestamp: Date.now()
|
|
12837
|
+
};
|
|
12838
|
+
// Clear any pending overlay dismiss
|
|
12839
|
+
if (this.overlayDismissTimeout) {
|
|
12840
|
+
clearTimeout(this.overlayDismissTimeout);
|
|
12841
|
+
this.overlayDismissTimeout = null;
|
|
12842
|
+
}
|
|
12843
|
+
// Show overlay if this is the first item
|
|
12844
|
+
if (this.loadingItemsSignal().length === 0) {
|
|
12845
|
+
this.overlayVisibleSignal.set(true);
|
|
12846
|
+
}
|
|
12847
|
+
this.loadingItemsSignal.update(items => [...items, item]);
|
|
12848
|
+
return id;
|
|
12849
|
+
}
|
|
12850
|
+
/**
|
|
12851
|
+
* Dismiss a loading item from the queue with animation
|
|
12852
|
+
* @param id The ID of the loading item to dismiss
|
|
12853
|
+
*/
|
|
12854
|
+
dismiss(id) {
|
|
12855
|
+
// First, mark the item as dismissing to trigger animation
|
|
12856
|
+
this.loadingItemsSignal.update(items => items.map(item => item.id === id ? { ...item, dismissing: true } : item));
|
|
12857
|
+
// After animation completes, remove the item
|
|
12858
|
+
setTimeout(() => {
|
|
12859
|
+
this.loadingItemsSignal.update(items => items.filter(item => item.id !== id));
|
|
12860
|
+
// If no more items, start overlay dismiss
|
|
12861
|
+
if (this.loadingItemsSignal().length === 0) {
|
|
12862
|
+
this.overlayDismissTimeout = setTimeout(() => {
|
|
12863
|
+
this.overlayVisibleSignal.set(false);
|
|
12864
|
+
}, this.overlayAnimationDuration);
|
|
12865
|
+
}
|
|
12866
|
+
}, this.itemDismissAnimationDuration);
|
|
12867
|
+
}
|
|
12868
|
+
/**
|
|
12869
|
+
* Clear all loading items immediately
|
|
12870
|
+
*/
|
|
12871
|
+
clear() {
|
|
12872
|
+
this.loadingItemsSignal.set([]);
|
|
12873
|
+
if (this.overlayDismissTimeout) {
|
|
12874
|
+
clearTimeout(this.overlayDismissTimeout);
|
|
12875
|
+
}
|
|
12876
|
+
this.overlayDismissTimeout = setTimeout(() => {
|
|
12877
|
+
this.overlayVisibleSignal.set(false);
|
|
12878
|
+
}, this.overlayAnimationDuration);
|
|
12879
|
+
}
|
|
12880
|
+
/**
|
|
12881
|
+
* Update the message for an existing loading item
|
|
12882
|
+
* @param id The ID of the loading item
|
|
12883
|
+
* @param message The new message
|
|
12884
|
+
*/
|
|
12885
|
+
updateMessage(id, message) {
|
|
12886
|
+
this.loadingItemsSignal.update(items => items.map(item => item.id === id ? { ...item, message } : item));
|
|
12887
|
+
}
|
|
12888
|
+
/**
|
|
12889
|
+
* Generate a unique ID for a loading item
|
|
12890
|
+
*/
|
|
12891
|
+
generateId() {
|
|
12892
|
+
return `fsl_${Date.now()}_${++this.idCounter}`;
|
|
12893
|
+
}
|
|
12894
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwFullScreenLoadingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
12895
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwFullScreenLoadingService, providedIn: 'root' });
|
|
12896
|
+
}
|
|
12897
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwFullScreenLoadingService, decorators: [{
|
|
12898
|
+
type: Injectable,
|
|
12899
|
+
args: [{
|
|
12900
|
+
providedIn: 'root'
|
|
12901
|
+
}]
|
|
12902
|
+
}], ctorParameters: () => [] });
|
|
12903
|
+
/**
|
|
12904
|
+
* RxJS operator that adds a loading message while the observable is active.
|
|
12905
|
+
* The message is dismissed when the observable emits its first value (for single-emission observables)
|
|
12906
|
+
* or when it completes/errors.
|
|
12907
|
+
*
|
|
12908
|
+
* @param message The loading message to display
|
|
12909
|
+
* @param options Configuration options
|
|
12910
|
+
* @returns A MonoTypeOperatorFunction that can be used with .pipe()
|
|
12911
|
+
*
|
|
12912
|
+
* @example
|
|
12913
|
+
* ```typescript
|
|
12914
|
+
* // Basic usage
|
|
12915
|
+
* this.http.get('/api/data')
|
|
12916
|
+
* .pipe(loading('Loading data...'))
|
|
12917
|
+
* .subscribe(data => this.data = data);
|
|
12918
|
+
*
|
|
12919
|
+
* // With multiple operations
|
|
12920
|
+
* this.service.doAThing()
|
|
12921
|
+
* .pipe(loading('Processing...'))
|
|
12922
|
+
* .subscribe(result => console.log(result));
|
|
12923
|
+
* ```
|
|
12924
|
+
*/
|
|
12925
|
+
function loading(message, options) {
|
|
12926
|
+
const dismissOnComplete = options?.dismissOnComplete ?? false;
|
|
12927
|
+
return (source) => {
|
|
12928
|
+
let loadingId = null;
|
|
12929
|
+
let dismissed = false;
|
|
12930
|
+
let service = options?.service ?? null;
|
|
12931
|
+
// Lazy inject the service if not provided
|
|
12932
|
+
const getService = () => {
|
|
12933
|
+
if (!service) {
|
|
12934
|
+
// Dynamic import to get the injector
|
|
12935
|
+
const injector = globalThis.__amwFullScreenLoadingInjector;
|
|
12936
|
+
if (injector) {
|
|
12937
|
+
service = injector.get(AmwFullScreenLoadingService);
|
|
12938
|
+
}
|
|
12939
|
+
else {
|
|
12940
|
+
throw new Error('AmwFullScreenLoadingService not available. ' +
|
|
12941
|
+
'Make sure AmwFullScreenLoadingComponent is included in your app.');
|
|
12942
|
+
}
|
|
12943
|
+
}
|
|
12944
|
+
return service;
|
|
12945
|
+
};
|
|
12946
|
+
return new Observable(subscriber => {
|
|
12947
|
+
// Add loading item when subscription starts
|
|
12948
|
+
try {
|
|
12949
|
+
loadingId = getService().add(message);
|
|
12950
|
+
}
|
|
12951
|
+
catch (e) {
|
|
12952
|
+
console.warn('Full screen loading service not available:', e);
|
|
12953
|
+
}
|
|
12954
|
+
const dismissLoading = () => {
|
|
12955
|
+
if (!dismissed && loadingId) {
|
|
12956
|
+
dismissed = true;
|
|
12957
|
+
try {
|
|
12958
|
+
getService().dismiss(loadingId);
|
|
12959
|
+
}
|
|
12960
|
+
catch (e) {
|
|
12961
|
+
// Service may not be available
|
|
12962
|
+
}
|
|
12963
|
+
}
|
|
12964
|
+
};
|
|
12965
|
+
const subscription = source.subscribe({
|
|
12966
|
+
next: (value) => {
|
|
12967
|
+
if (!dismissOnComplete) {
|
|
12968
|
+
dismissLoading();
|
|
12969
|
+
}
|
|
12970
|
+
subscriber.next(value);
|
|
12971
|
+
},
|
|
12972
|
+
error: (err) => {
|
|
12973
|
+
dismissLoading();
|
|
12974
|
+
subscriber.error(err);
|
|
12975
|
+
},
|
|
12976
|
+
complete: () => {
|
|
12977
|
+
dismissLoading();
|
|
12978
|
+
subscriber.complete();
|
|
12979
|
+
}
|
|
12980
|
+
});
|
|
12981
|
+
// Cleanup on unsubscribe
|
|
12982
|
+
return () => {
|
|
12983
|
+
dismissLoading();
|
|
12984
|
+
subscription.unsubscribe();
|
|
12985
|
+
};
|
|
12986
|
+
});
|
|
12987
|
+
};
|
|
12988
|
+
}
|
|
12989
|
+
|
|
12990
|
+
/**
|
|
12991
|
+
* AMW Full Screen Loading Component
|
|
12992
|
+
*
|
|
12993
|
+
* Displays a full-screen loading overlay with multiple animated messages.
|
|
12994
|
+
* Each message is keyed to an observable and dismisses with a smooth animation
|
|
12995
|
+
* when the observable completes.
|
|
12996
|
+
*
|
|
12997
|
+
* Add this component once at the root of your application (e.g., in app.component.html):
|
|
12998
|
+
*
|
|
12999
|
+
* @example
|
|
13000
|
+
* ```html
|
|
13001
|
+
* <!-- In app.component.html -->
|
|
13002
|
+
* <router-outlet></router-outlet>
|
|
13003
|
+
* <amw-full-screen-loading></amw-full-screen-loading>
|
|
13004
|
+
* ```
|
|
13005
|
+
*
|
|
13006
|
+
* Then use the loading() operator in your services:
|
|
13007
|
+
*
|
|
13008
|
+
* @example
|
|
13009
|
+
* ```typescript
|
|
13010
|
+
* // In your component/service
|
|
13011
|
+
* import { loading } from '@angular-material-wrap';
|
|
13012
|
+
*
|
|
13013
|
+
* this.userService.getUser(id)
|
|
13014
|
+
* .pipe(loading('Loading user profile...'))
|
|
13015
|
+
* .subscribe(user => this.user = user);
|
|
13016
|
+
*
|
|
13017
|
+
* // Multiple simultaneous operations
|
|
13018
|
+
* this.dataService.fetchData()
|
|
13019
|
+
* .pipe(loading('Fetching data...'))
|
|
13020
|
+
* .subscribe();
|
|
13021
|
+
*
|
|
13022
|
+
* this.otherService.process()
|
|
13023
|
+
* .pipe(loading('Processing...'))
|
|
13024
|
+
* .subscribe();
|
|
13025
|
+
* ```
|
|
13026
|
+
*
|
|
13027
|
+
* Features:
|
|
13028
|
+
* - Smooth 300ms transitions for overlay appearance/disappearance
|
|
13029
|
+
* - Multiple messages can display simultaneously
|
|
13030
|
+
* - Messages dismiss with a slide-right + fade animation
|
|
13031
|
+
* - Centered spinner and message display
|
|
13032
|
+
* - Subtle pulse animation on the spinner
|
|
13033
|
+
*/
|
|
13034
|
+
class AmwFullScreenLoadingComponent {
|
|
13035
|
+
injector = inject(Injector);
|
|
13036
|
+
loadingService = inject(AmwFullScreenLoadingService);
|
|
13037
|
+
/** Computed signal for loading items */
|
|
13038
|
+
items = computed(() => this.loadingService.loadingItems(), ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
13039
|
+
/** Computed signal for overlay visibility */
|
|
13040
|
+
isVisible = computed(() => this.loadingService.overlayVisible(), ...(ngDevMode ? [{ debugName: "isVisible" }] : []));
|
|
13041
|
+
/** Computed signal for whether loading is active (items exist) */
|
|
13042
|
+
isLoading = computed(() => this.loadingService.isLoading(), ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
13043
|
+
ngOnInit() {
|
|
13044
|
+
// Register the injector globally for the loading() operator to use
|
|
13045
|
+
globalThis.__amwFullScreenLoadingInjector = this.injector;
|
|
13046
|
+
}
|
|
13047
|
+
ngOnDestroy() {
|
|
13048
|
+
// Clean up global injector reference
|
|
13049
|
+
if (globalThis.__amwFullScreenLoadingInjector === this.injector) {
|
|
13050
|
+
delete globalThis.__amwFullScreenLoadingInjector;
|
|
13051
|
+
}
|
|
13052
|
+
}
|
|
13053
|
+
/**
|
|
13054
|
+
* Track by function for ngFor
|
|
13055
|
+
*/
|
|
13056
|
+
trackByItem(index, item) {
|
|
13057
|
+
return item.id;
|
|
13058
|
+
}
|
|
13059
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwFullScreenLoadingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13060
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: AmwFullScreenLoadingComponent, isStandalone: true, selector: "amw-full-screen-loading", ngImport: i0, template: "@if (isVisible()) {\n<div class=\"amw-full-screen-loading\" [class.amw-full-screen-loading--visible]=\"isLoading()\"\n [class.amw-full-screen-loading--hiding]=\"!isLoading()\">\n <!-- Overlay backdrop -->\n <div class=\"amw-full-screen-loading__backdrop\"></div>\n\n <!-- Spinner container (fixed position) -->\n <div class=\"amw-full-screen-loading__content\">\n <div class=\"amw-fsl-spinner\">\n <div class=\"amw-fsl-spinner__ring amw-fsl-spinner__ring--1\"></div>\n <div class=\"amw-fsl-spinner__ring amw-fsl-spinner__ring--2\"></div>\n </div>\n </div>\n\n <!-- Messages container (fixed position below spinner) -->\n <div class=\"amw-full-screen-loading__messages\">\n @for (item of items(); track trackByItem($index, item)) {\n <div class=\"amw-full-screen-loading__message\"\n [class.amw-full-screen-loading__message--dismissing]=\"item.dismissing\">\n <span class=\"amw-full-screen-loading__message-text\">{{ item.message }}</span>\n </div>\n }\n </div>\n</div>\n}", styles: [".amw-full-screen-loading{position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;display:flex;align-items:center;justify-content:center;pointer-events:all;opacity:0}.amw-full-screen-loading--visible{animation:amw-fsl-fade-in .6s cubic-bezier(0,0,.2,1) forwards}.amw-full-screen-loading--hiding{animation:amw-fsl-fade-out .5s cubic-bezier(.4,0,1,1) forwards}.amw-full-screen-loading__backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);transition:backdrop-filter .6s ease-out}.amw-full-screen-loading__content{position:absolute;top:30%;left:50%;transform:translate(-50%);z-index:1;display:flex;flex-direction:column;align-items:center}.amw-full-screen-loading__messages{position:absolute;top:calc(30% + 375px);left:50%;transform:translate(-50%);z-index:1;display:flex;flex-direction:column;align-items:center;gap:8px;min-width:200px}.amw-full-screen-loading__message{display:flex;align-items:center;justify-content:center;padding:12px 24px;background-color:#fffffff2;border-radius:24px;opacity:1;transform:translate(0);box-shadow:0 4px 20px #00000026;transition:opacity .4s cubic-bezier(.4,0,.2,1),transform .4s cubic-bezier(.4,0,.2,1);animation:amw-fsl-message-enter .45s cubic-bezier(0,0,.2,1)}.amw-full-screen-loading__message--dismissing{opacity:0;transform:translate(40px);transition:opacity .5s cubic-bezier(.4,0,1,1),transform .5s cubic-bezier(.4,0,1,1)}.amw-full-screen-loading__message-text{font-size:14px;font-weight:500;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:400px}.amw-fsl-spinner{position:relative;width:350px;height:350px}.amw-fsl-spinner__ring{position:absolute;top:50%;left:50%;border-radius:50%;box-sizing:border-box}.amw-fsl-spinner__ring--1{width:360px;height:360px;margin-left:-180px;margin-top:-180px;background:conic-gradient(var(--mdc-theme-primary, #6200ee) 0deg 100deg,transparent 100deg 120deg,var(--mdc-theme-primary, #6200ee) 120deg 220deg,transparent 220deg 240deg,var(--mdc-theme-primary, #6200ee) 240deg 340deg,transparent 340deg 360deg);mask:radial-gradient(circle,transparent 50%,black 50%);-webkit-mask:radial-gradient(circle,transparent 50%,black 50%);animation:amw-fsl-rotate-cw-1 25s cubic-bezier(.4,0,.2,1) infinite}.amw-fsl-spinner__ring--2{width:250px;height:250px;margin-left:-125px;margin-top:-125px;background:conic-gradient(var(--mdc-theme-secondary, #03dac6) 0deg 92deg,transparent 92deg 120deg,var(--mdc-theme-secondary, #03dac6) 120deg 212deg,transparent 212deg 240deg,var(--mdc-theme-secondary, #03dac6) 240deg 332deg,transparent 332deg 360deg);mask:radial-gradient(circle,transparent 40%,black 40%);-webkit-mask:radial-gradient(circle,transparent 40%,black 40%);animation:amw-fsl-rotate-ccw-1 22s cubic-bezier(.25,.1,.25,1) infinite}@keyframes amw-fsl-rotate-cw-1{0%{transform:rotate(0)}25%{transform:rotate(120deg)}50%{transform:rotate(180deg)}75%{transform:rotate(300deg)}to{transform:rotate(360deg)}}@keyframes amw-fsl-rotate-ccw-1{0%{transform:rotate(0)}20%{transform:rotate(-90deg)}40%{transform:rotate(-120deg)}60%{transform:rotate(-240deg)}80%{transform:rotate(-300deg)}to{transform:rotate(-360deg)}}@keyframes amw-fsl-fade-in{0%{opacity:0}to{opacity:1}}@keyframes amw-fsl-fade-out{0%{opacity:1}to{opacity:0}}@keyframes amw-fsl-message-enter{0%{opacity:0;transform:translateY(15px) scale(.98)}60%{opacity:.8;transform:translateY(-2px) scale(1)}to{opacity:1;transform:translateY(0) scale(1)}}.dark-theme .amw-full-screen-loading__backdrop{background-color:#000000bf}.dark-theme .amw-full-screen-loading__message{background-color:#1e1e1ef2;box-shadow:0 4px 20px #0006}.dark-theme .amw-full-screen-loading__message-text{color:#fff}.dark-theme .amw-fsl-spinner__ring--1{background:conic-gradient(var(--mdc-theme-primary, #bb86fc) 0deg 100deg,transparent 100deg 120deg,var(--mdc-theme-primary, #bb86fc) 120deg 220deg,transparent 220deg 240deg,var(--mdc-theme-primary, #bb86fc) 240deg 340deg,transparent 340deg 360deg)}.dark-theme .amw-fsl-spinner__ring--2{background:conic-gradient(var(--mdc-theme-secondary, #03dac6) 0deg 92deg,transparent 92deg 120deg,var(--mdc-theme-secondary, #03dac6) 120deg 212deg,transparent 212deg 240deg,var(--mdc-theme-secondary, #03dac6) 240deg 332deg,transparent 332deg 360deg)}@media(prefers-reduced-motion:reduce){.amw-full-screen-loading--visible{animation:amw-fsl-fade-in .4s linear forwards}.amw-full-screen-loading--hiding{animation:amw-fsl-fade-out .4s linear forwards}.amw-full-screen-loading__message{transition:opacity .2s linear;animation:none}.amw-full-screen-loading__message--dismissing{transform:none;transition:opacity .3s linear}.amw-fsl-spinner__ring{animation-duration:20s!important;animation-timing-function:linear!important}.amw-fsl-spinner__ring--1{animation-name:amw-fsl-rotate-simple-cw}.amw-fsl-spinner__ring--2{animation-name:amw-fsl-rotate-simple-ccw}@keyframes amw-fsl-rotate-simple-cw{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes amw-fsl-rotate-simple-ccw{0%{transform:rotate(0)}to{transform:rotate(-360deg)}}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None });
|
|
13061
|
+
}
|
|
13062
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwFullScreenLoadingComponent, decorators: [{
|
|
13063
|
+
type: Component,
|
|
13064
|
+
args: [{ selector: 'amw-full-screen-loading', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, template: "@if (isVisible()) {\n<div class=\"amw-full-screen-loading\" [class.amw-full-screen-loading--visible]=\"isLoading()\"\n [class.amw-full-screen-loading--hiding]=\"!isLoading()\">\n <!-- Overlay backdrop -->\n <div class=\"amw-full-screen-loading__backdrop\"></div>\n\n <!-- Spinner container (fixed position) -->\n <div class=\"amw-full-screen-loading__content\">\n <div class=\"amw-fsl-spinner\">\n <div class=\"amw-fsl-spinner__ring amw-fsl-spinner__ring--1\"></div>\n <div class=\"amw-fsl-spinner__ring amw-fsl-spinner__ring--2\"></div>\n </div>\n </div>\n\n <!-- Messages container (fixed position below spinner) -->\n <div class=\"amw-full-screen-loading__messages\">\n @for (item of items(); track trackByItem($index, item)) {\n <div class=\"amw-full-screen-loading__message\"\n [class.amw-full-screen-loading__message--dismissing]=\"item.dismissing\">\n <span class=\"amw-full-screen-loading__message-text\">{{ item.message }}</span>\n </div>\n }\n </div>\n</div>\n}", styles: [".amw-full-screen-loading{position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;display:flex;align-items:center;justify-content:center;pointer-events:all;opacity:0}.amw-full-screen-loading--visible{animation:amw-fsl-fade-in .6s cubic-bezier(0,0,.2,1) forwards}.amw-full-screen-loading--hiding{animation:amw-fsl-fade-out .5s cubic-bezier(.4,0,1,1) forwards}.amw-full-screen-loading__backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#0009;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);transition:backdrop-filter .6s ease-out}.amw-full-screen-loading__content{position:absolute;top:30%;left:50%;transform:translate(-50%);z-index:1;display:flex;flex-direction:column;align-items:center}.amw-full-screen-loading__messages{position:absolute;top:calc(30% + 375px);left:50%;transform:translate(-50%);z-index:1;display:flex;flex-direction:column;align-items:center;gap:8px;min-width:200px}.amw-full-screen-loading__message{display:flex;align-items:center;justify-content:center;padding:12px 24px;background-color:#fffffff2;border-radius:24px;opacity:1;transform:translate(0);box-shadow:0 4px 20px #00000026;transition:opacity .4s cubic-bezier(.4,0,.2,1),transform .4s cubic-bezier(.4,0,.2,1);animation:amw-fsl-message-enter .45s cubic-bezier(0,0,.2,1)}.amw-full-screen-loading__message--dismissing{opacity:0;transform:translate(40px);transition:opacity .5s cubic-bezier(.4,0,1,1),transform .5s cubic-bezier(.4,0,1,1)}.amw-full-screen-loading__message-text{font-size:14px;font-weight:500;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:400px}.amw-fsl-spinner{position:relative;width:350px;height:350px}.amw-fsl-spinner__ring{position:absolute;top:50%;left:50%;border-radius:50%;box-sizing:border-box}.amw-fsl-spinner__ring--1{width:360px;height:360px;margin-left:-180px;margin-top:-180px;background:conic-gradient(var(--mdc-theme-primary, #6200ee) 0deg 100deg,transparent 100deg 120deg,var(--mdc-theme-primary, #6200ee) 120deg 220deg,transparent 220deg 240deg,var(--mdc-theme-primary, #6200ee) 240deg 340deg,transparent 340deg 360deg);mask:radial-gradient(circle,transparent 50%,black 50%);-webkit-mask:radial-gradient(circle,transparent 50%,black 50%);animation:amw-fsl-rotate-cw-1 25s cubic-bezier(.4,0,.2,1) infinite}.amw-fsl-spinner__ring--2{width:250px;height:250px;margin-left:-125px;margin-top:-125px;background:conic-gradient(var(--mdc-theme-secondary, #03dac6) 0deg 92deg,transparent 92deg 120deg,var(--mdc-theme-secondary, #03dac6) 120deg 212deg,transparent 212deg 240deg,var(--mdc-theme-secondary, #03dac6) 240deg 332deg,transparent 332deg 360deg);mask:radial-gradient(circle,transparent 40%,black 40%);-webkit-mask:radial-gradient(circle,transparent 40%,black 40%);animation:amw-fsl-rotate-ccw-1 22s cubic-bezier(.25,.1,.25,1) infinite}@keyframes amw-fsl-rotate-cw-1{0%{transform:rotate(0)}25%{transform:rotate(120deg)}50%{transform:rotate(180deg)}75%{transform:rotate(300deg)}to{transform:rotate(360deg)}}@keyframes amw-fsl-rotate-ccw-1{0%{transform:rotate(0)}20%{transform:rotate(-90deg)}40%{transform:rotate(-120deg)}60%{transform:rotate(-240deg)}80%{transform:rotate(-300deg)}to{transform:rotate(-360deg)}}@keyframes amw-fsl-fade-in{0%{opacity:0}to{opacity:1}}@keyframes amw-fsl-fade-out{0%{opacity:1}to{opacity:0}}@keyframes amw-fsl-message-enter{0%{opacity:0;transform:translateY(15px) scale(.98)}60%{opacity:.8;transform:translateY(-2px) scale(1)}to{opacity:1;transform:translateY(0) scale(1)}}.dark-theme .amw-full-screen-loading__backdrop{background-color:#000000bf}.dark-theme .amw-full-screen-loading__message{background-color:#1e1e1ef2;box-shadow:0 4px 20px #0006}.dark-theme .amw-full-screen-loading__message-text{color:#fff}.dark-theme .amw-fsl-spinner__ring--1{background:conic-gradient(var(--mdc-theme-primary, #bb86fc) 0deg 100deg,transparent 100deg 120deg,var(--mdc-theme-primary, #bb86fc) 120deg 220deg,transparent 220deg 240deg,var(--mdc-theme-primary, #bb86fc) 240deg 340deg,transparent 340deg 360deg)}.dark-theme .amw-fsl-spinner__ring--2{background:conic-gradient(var(--mdc-theme-secondary, #03dac6) 0deg 92deg,transparent 92deg 120deg,var(--mdc-theme-secondary, #03dac6) 120deg 212deg,transparent 212deg 240deg,var(--mdc-theme-secondary, #03dac6) 240deg 332deg,transparent 332deg 360deg)}@media(prefers-reduced-motion:reduce){.amw-full-screen-loading--visible{animation:amw-fsl-fade-in .4s linear forwards}.amw-full-screen-loading--hiding{animation:amw-fsl-fade-out .4s linear forwards}.amw-full-screen-loading__message{transition:opacity .2s linear;animation:none}.amw-full-screen-loading__message--dismissing{transform:none;transition:opacity .3s linear}.amw-fsl-spinner__ring{animation-duration:20s!important;animation-timing-function:linear!important}.amw-fsl-spinner__ring--1{animation-name:amw-fsl-rotate-simple-cw}.amw-fsl-spinner__ring--2{animation-name:amw-fsl-rotate-simple-ccw}@keyframes amw-fsl-rotate-simple-cw{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes amw-fsl-rotate-simple-ccw{0%{transform:rotate(0)}to{transform:rotate(-360deg)}}}\n"] }]
|
|
13065
|
+
}] });
|
|
13066
|
+
|
|
13067
|
+
const DEFAULT_CONFIG = {
|
|
13068
|
+
maxErrors: 50,
|
|
13069
|
+
autoDismissAfter: 0,
|
|
13070
|
+
logToConsole: true,
|
|
13071
|
+
trackHistory: true,
|
|
13072
|
+
maxHistory: 100
|
|
13073
|
+
};
|
|
13074
|
+
/**
|
|
13075
|
+
* AMW Error State Service
|
|
13076
|
+
*
|
|
13077
|
+
* Provides centralized error state management for components and applications.
|
|
13078
|
+
* Components can create error contexts, add/remove errors, and display them
|
|
13079
|
+
* in a consistent manner.
|
|
13080
|
+
*
|
|
13081
|
+
* @example
|
|
13082
|
+
* ```typescript
|
|
13083
|
+
* // In your component
|
|
13084
|
+
* export class MyComponent implements OnInit, OnDestroy {
|
|
13085
|
+
* private errorContext!: ErrorContext;
|
|
13086
|
+
*
|
|
13087
|
+
* constructor(private errorStateService: AmwErrorStateService) {}
|
|
13088
|
+
*
|
|
13089
|
+
* ngOnInit() {
|
|
13090
|
+
* this.errorContext = this.errorStateService.createContext({
|
|
13091
|
+
* autoDismissAfter: 5000
|
|
13092
|
+
* });
|
|
13093
|
+
* }
|
|
13094
|
+
*
|
|
13095
|
+
* ngOnDestroy() {
|
|
13096
|
+
* this.errorStateService.destroyContext(this.errorContext.id);
|
|
13097
|
+
* }
|
|
13098
|
+
*
|
|
13099
|
+
* handleApiError(error: HttpErrorResponse) {
|
|
13100
|
+
* this.errorStateService.addError(this.errorContext.id, {
|
|
13101
|
+
* message: error.message,
|
|
13102
|
+
* code: error.status.toString(),
|
|
13103
|
+
* severity: 'error',
|
|
13104
|
+
* source: 'API',
|
|
13105
|
+
* originalError: error,
|
|
13106
|
+
* retryAction: () => this.retryRequest()
|
|
13107
|
+
* });
|
|
13108
|
+
* }
|
|
13109
|
+
* }
|
|
13110
|
+
* ```
|
|
13111
|
+
*/
|
|
13112
|
+
class AmwErrorStateService {
|
|
13113
|
+
/** Map of all error contexts by ID */
|
|
13114
|
+
contexts = new Map();
|
|
13115
|
+
/** Global error history */
|
|
13116
|
+
errorHistory = signal([], ...(ngDevMode ? [{ debugName: "errorHistory" }] : []));
|
|
13117
|
+
/** Counter for generating unique IDs */
|
|
13118
|
+
idCounter = 0;
|
|
13119
|
+
/** Map of auto-dismiss timers */
|
|
13120
|
+
dismissTimers = new Map();
|
|
13121
|
+
/** Subject for error events */
|
|
13122
|
+
errorSubject = new Subject();
|
|
13123
|
+
/** Observable for error events */
|
|
13124
|
+
errors$ = this.errorSubject.asObservable();
|
|
13125
|
+
/** Global config */
|
|
13126
|
+
globalConfig = { ...DEFAULT_CONFIG };
|
|
13127
|
+
constructor() { }
|
|
13128
|
+
/**
|
|
13129
|
+
* Configure global error state settings
|
|
13130
|
+
*/
|
|
13131
|
+
configure(config) {
|
|
13132
|
+
this.globalConfig = { ...this.globalConfig, ...config };
|
|
13133
|
+
}
|
|
13134
|
+
/**
|
|
13135
|
+
* Create a new error context for a component
|
|
13136
|
+
* @param config Optional configuration
|
|
13137
|
+
* @returns The created error context
|
|
13138
|
+
*/
|
|
13139
|
+
createContext(config) {
|
|
13140
|
+
const id = this.generateId();
|
|
13141
|
+
const errors = signal([], ...(ngDevMode ? [{ debugName: "errors" }] : []));
|
|
13142
|
+
const mergedConfig = { ...this.globalConfig, ...config };
|
|
13143
|
+
const hasErrors = computed(() => errors().some(e => e.severity === 'error' && !e.dismissed), ...(ngDevMode ? [{ debugName: "hasErrors" }] : []));
|
|
13144
|
+
const hasWarnings = computed(() => errors().some(e => e.severity === 'warning' && !e.dismissed), ...(ngDevMode ? [{ debugName: "hasWarnings" }] : []));
|
|
13145
|
+
const errorCount = computed(() => errors().filter(e => !e.dismissed).length, ...(ngDevMode ? [{ debugName: "errorCount" }] : []));
|
|
13146
|
+
const context = {
|
|
13147
|
+
id,
|
|
13148
|
+
errors,
|
|
13149
|
+
hasErrors,
|
|
13150
|
+
hasWarnings,
|
|
13151
|
+
errorCount,
|
|
13152
|
+
config: mergedConfig
|
|
13153
|
+
};
|
|
13154
|
+
this.contexts.set(id, context);
|
|
13155
|
+
return context;
|
|
13156
|
+
}
|
|
13157
|
+
/**
|
|
13158
|
+
* Destroy an error context and clean up timers
|
|
13159
|
+
* @param contextId The context ID to destroy
|
|
13160
|
+
*/
|
|
13161
|
+
destroyContext(contextId) {
|
|
13162
|
+
// Clean up any dismiss timers
|
|
13163
|
+
const context = this.contexts.get(contextId);
|
|
13164
|
+
if (context) {
|
|
13165
|
+
context.errors().forEach(error => {
|
|
13166
|
+
const timerKey = `${contextId}_${error.id}`;
|
|
13167
|
+
const timer = this.dismissTimers.get(timerKey);
|
|
13168
|
+
if (timer) {
|
|
13169
|
+
clearTimeout(timer);
|
|
13170
|
+
this.dismissTimers.delete(timerKey);
|
|
13171
|
+
}
|
|
13172
|
+
});
|
|
13173
|
+
}
|
|
13174
|
+
this.contexts.delete(contextId);
|
|
13175
|
+
}
|
|
13176
|
+
/**
|
|
13177
|
+
* Add an error to a context
|
|
13178
|
+
* @param contextId The context ID
|
|
13179
|
+
* @param error The error to add (without ID, one will be generated)
|
|
13180
|
+
* @returns The error ID
|
|
13181
|
+
*/
|
|
13182
|
+
addError(contextId, error) {
|
|
13183
|
+
const context = this.contexts.get(contextId);
|
|
13184
|
+
if (!context) {
|
|
13185
|
+
throw new Error(`Error context ${contextId} not found`);
|
|
13186
|
+
}
|
|
13187
|
+
const errorId = error.id || this.generateErrorId();
|
|
13188
|
+
const fullError = {
|
|
13189
|
+
...error,
|
|
13190
|
+
id: errorId,
|
|
13191
|
+
timestamp: Date.now(),
|
|
13192
|
+
dismissed: false,
|
|
13193
|
+
dismissible: error.dismissible ?? true
|
|
13194
|
+
};
|
|
13195
|
+
// Log to console if configured
|
|
13196
|
+
if (context.config.logToConsole) {
|
|
13197
|
+
this.logError(fullError);
|
|
13198
|
+
}
|
|
13199
|
+
// Check max errors
|
|
13200
|
+
let currentErrors = context.errors();
|
|
13201
|
+
if (context.config.maxErrors && currentErrors.length >= context.config.maxErrors) {
|
|
13202
|
+
// Remove oldest error
|
|
13203
|
+
currentErrors = currentErrors.slice(1);
|
|
13204
|
+
}
|
|
13205
|
+
// Add new error
|
|
13206
|
+
context.errors.set([...currentErrors, fullError]);
|
|
13207
|
+
// Add to history if configured
|
|
13208
|
+
if (context.config.trackHistory) {
|
|
13209
|
+
this.addToHistory(fullError);
|
|
13210
|
+
}
|
|
13211
|
+
// Emit error event
|
|
13212
|
+
this.errorSubject.next(fullError);
|
|
13213
|
+
// Set up auto-dismiss if configured
|
|
13214
|
+
if (context.config.autoDismissAfter && context.config.autoDismissAfter > 0) {
|
|
13215
|
+
this.setupAutoDismiss(contextId, errorId, context.config.autoDismissAfter);
|
|
13216
|
+
}
|
|
13217
|
+
return errorId;
|
|
13218
|
+
}
|
|
13219
|
+
/**
|
|
13220
|
+
* Add an error from a caught exception
|
|
13221
|
+
* @param contextId The context ID
|
|
13222
|
+
* @param error The caught error
|
|
13223
|
+
* @param options Additional options
|
|
13224
|
+
*/
|
|
13225
|
+
addFromException(contextId, error, options) {
|
|
13226
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13227
|
+
const code = error instanceof Error ? error.name : 'UNKNOWN';
|
|
13228
|
+
return this.addError(contextId, {
|
|
13229
|
+
message,
|
|
13230
|
+
code,
|
|
13231
|
+
severity: options?.severity || 'error',
|
|
13232
|
+
source: options?.source,
|
|
13233
|
+
field: options?.field,
|
|
13234
|
+
originalError: error,
|
|
13235
|
+
retryAction: options?.retryAction
|
|
13236
|
+
});
|
|
13237
|
+
}
|
|
13238
|
+
/**
|
|
13239
|
+
* Dismiss an error
|
|
13240
|
+
* @param contextId The context ID
|
|
13241
|
+
* @param errorId The error ID to dismiss
|
|
13242
|
+
*/
|
|
13243
|
+
dismissError(contextId, errorId) {
|
|
13244
|
+
const context = this.contexts.get(contextId);
|
|
13245
|
+
if (!context)
|
|
13246
|
+
return;
|
|
13247
|
+
context.errors.update(errors => errors.map(e => e.id === errorId ? { ...e, dismissed: true } : e));
|
|
13248
|
+
// Clear auto-dismiss timer
|
|
13249
|
+
const timerKey = `${contextId}_${errorId}`;
|
|
13250
|
+
const timer = this.dismissTimers.get(timerKey);
|
|
13251
|
+
if (timer) {
|
|
13252
|
+
clearTimeout(timer);
|
|
13253
|
+
this.dismissTimers.delete(timerKey);
|
|
13254
|
+
}
|
|
13255
|
+
}
|
|
13256
|
+
/**
|
|
13257
|
+
* Remove an error completely
|
|
13258
|
+
* @param contextId The context ID
|
|
13259
|
+
* @param errorId The error ID to remove
|
|
13260
|
+
*/
|
|
13261
|
+
removeError(contextId, errorId) {
|
|
13262
|
+
const context = this.contexts.get(contextId);
|
|
13263
|
+
if (!context)
|
|
13264
|
+
return;
|
|
13265
|
+
context.errors.update(errors => errors.filter(e => e.id !== errorId));
|
|
13266
|
+
// Clear auto-dismiss timer
|
|
13267
|
+
const timerKey = `${contextId}_${errorId}`;
|
|
13268
|
+
const timer = this.dismissTimers.get(timerKey);
|
|
13269
|
+
if (timer) {
|
|
13270
|
+
clearTimeout(timer);
|
|
13271
|
+
this.dismissTimers.delete(timerKey);
|
|
13272
|
+
}
|
|
13273
|
+
}
|
|
13274
|
+
/**
|
|
13275
|
+
* Clear all errors from a context
|
|
13276
|
+
* @param contextId The context ID
|
|
13277
|
+
*/
|
|
13278
|
+
clearErrors(contextId) {
|
|
13279
|
+
const context = this.contexts.get(contextId);
|
|
13280
|
+
if (!context)
|
|
13281
|
+
return;
|
|
13282
|
+
// Clear all timers for this context
|
|
13283
|
+
context.errors().forEach(error => {
|
|
13284
|
+
const timerKey = `${contextId}_${error.id}`;
|
|
13285
|
+
const timer = this.dismissTimers.get(timerKey);
|
|
13286
|
+
if (timer) {
|
|
13287
|
+
clearTimeout(timer);
|
|
13288
|
+
this.dismissTimers.delete(timerKey);
|
|
13289
|
+
}
|
|
13290
|
+
});
|
|
13291
|
+
context.errors.set([]);
|
|
13292
|
+
}
|
|
13293
|
+
/**
|
|
13294
|
+
* Get all errors for a context
|
|
13295
|
+
* @param contextId The context ID
|
|
13296
|
+
* @returns Array of errors
|
|
13297
|
+
*/
|
|
13298
|
+
getErrors(contextId) {
|
|
13299
|
+
const context = this.contexts.get(contextId);
|
|
13300
|
+
return context?.errors() || [];
|
|
13301
|
+
}
|
|
13302
|
+
/**
|
|
13303
|
+
* Get active (non-dismissed) errors
|
|
13304
|
+
* @param contextId The context ID
|
|
13305
|
+
* @returns Array of active errors
|
|
13306
|
+
*/
|
|
13307
|
+
getActiveErrors(contextId) {
|
|
13308
|
+
return this.getErrors(contextId).filter(e => !e.dismissed);
|
|
13309
|
+
}
|
|
13310
|
+
/**
|
|
13311
|
+
* Get errors for a specific field
|
|
13312
|
+
* @param contextId The context ID
|
|
13313
|
+
* @param field The field name
|
|
13314
|
+
* @returns Array of errors for the field
|
|
13315
|
+
*/
|
|
13316
|
+
getFieldErrors(contextId, field) {
|
|
13317
|
+
return this.getErrors(contextId).filter(e => e.field === field && !e.dismissed);
|
|
13318
|
+
}
|
|
13319
|
+
/**
|
|
13320
|
+
* Check if a context has any active errors
|
|
13321
|
+
* @param contextId The context ID
|
|
13322
|
+
* @returns True if active errors exist
|
|
13323
|
+
*/
|
|
13324
|
+
hasActiveErrors(contextId) {
|
|
13325
|
+
return this.getActiveErrors(contextId).length > 0;
|
|
13326
|
+
}
|
|
13327
|
+
/**
|
|
13328
|
+
* Get the error history
|
|
13329
|
+
* @returns Array of historical errors
|
|
13330
|
+
*/
|
|
13331
|
+
getErrorHistory() {
|
|
13332
|
+
return this.errorHistory();
|
|
13333
|
+
}
|
|
13334
|
+
/**
|
|
13335
|
+
* Clear the error history
|
|
13336
|
+
*/
|
|
13337
|
+
clearHistory() {
|
|
13338
|
+
this.errorHistory.set([]);
|
|
13339
|
+
}
|
|
13340
|
+
/**
|
|
13341
|
+
* Get a context by ID
|
|
13342
|
+
* @param contextId The context ID
|
|
13343
|
+
* @returns The error context or undefined
|
|
13344
|
+
*/
|
|
13345
|
+
getContext(contextId) {
|
|
13346
|
+
return this.contexts.get(contextId);
|
|
13347
|
+
}
|
|
13348
|
+
/**
|
|
13349
|
+
* Retry an error's action if available
|
|
13350
|
+
* @param contextId The context ID
|
|
13351
|
+
* @param errorId The error ID
|
|
13352
|
+
*/
|
|
13353
|
+
retryError(contextId, errorId) {
|
|
13354
|
+
const context = this.contexts.get(contextId);
|
|
13355
|
+
if (!context)
|
|
13356
|
+
return;
|
|
13357
|
+
const error = context.errors().find(e => e.id === errorId);
|
|
13358
|
+
if (error?.retryAction) {
|
|
13359
|
+
// Dismiss the error before retrying
|
|
13360
|
+
this.dismissError(contextId, errorId);
|
|
13361
|
+
// Execute retry action
|
|
13362
|
+
error.retryAction();
|
|
13363
|
+
}
|
|
13364
|
+
}
|
|
13365
|
+
/**
|
|
13366
|
+
* Add error to history
|
|
13367
|
+
*/
|
|
13368
|
+
addToHistory(error) {
|
|
13369
|
+
let history = this.errorHistory();
|
|
13370
|
+
if (this.globalConfig.maxHistory && history.length >= this.globalConfig.maxHistory) {
|
|
13371
|
+
history = history.slice(1);
|
|
13372
|
+
}
|
|
13373
|
+
this.errorHistory.set([...history, error]);
|
|
13374
|
+
}
|
|
13375
|
+
/**
|
|
13376
|
+
* Log error to console
|
|
13377
|
+
*/
|
|
13378
|
+
logError(error) {
|
|
13379
|
+
const prefix = `[AMW Error State] [${error.severity.toUpperCase()}]`;
|
|
13380
|
+
const message = `${prefix} ${error.message}`;
|
|
13381
|
+
switch (error.severity) {
|
|
13382
|
+
case 'error':
|
|
13383
|
+
console.error(message, error.originalError || '');
|
|
13384
|
+
break;
|
|
13385
|
+
case 'warning':
|
|
13386
|
+
console.warn(message, error.originalError || '');
|
|
13387
|
+
break;
|
|
13388
|
+
case 'info':
|
|
13389
|
+
console.info(message);
|
|
13390
|
+
break;
|
|
13391
|
+
}
|
|
13392
|
+
}
|
|
13393
|
+
/**
|
|
13394
|
+
* Set up auto-dismiss timer
|
|
13395
|
+
*/
|
|
13396
|
+
setupAutoDismiss(contextId, errorId, duration) {
|
|
13397
|
+
const timerKey = `${contextId}_${errorId}`;
|
|
13398
|
+
const timer = setTimeout(() => {
|
|
13399
|
+
this.dismissError(contextId, errorId);
|
|
13400
|
+
this.dismissTimers.delete(timerKey);
|
|
13401
|
+
}, duration);
|
|
13402
|
+
this.dismissTimers.set(timerKey, timer);
|
|
13403
|
+
}
|
|
13404
|
+
generateId() {
|
|
13405
|
+
return `ec_${Date.now()}_${++this.idCounter}`;
|
|
13406
|
+
}
|
|
13407
|
+
generateErrorId() {
|
|
13408
|
+
return `err_${Date.now()}_${++this.idCounter}`;
|
|
13409
|
+
}
|
|
13410
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwErrorStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
13411
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwErrorStateService, providedIn: 'root' });
|
|
13412
|
+
}
|
|
13413
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwErrorStateService, decorators: [{
|
|
13414
|
+
type: Injectable,
|
|
13415
|
+
args: [{
|
|
13416
|
+
providedIn: 'root'
|
|
13417
|
+
}]
|
|
13418
|
+
}], ctorParameters: () => [] });
|
|
13419
|
+
/**
|
|
13420
|
+
* Mixin/base class functionality for components that need error state management
|
|
13421
|
+
* This can be used via composition or inheritance
|
|
13422
|
+
*/
|
|
13423
|
+
class ErrorStateMixin {
|
|
13424
|
+
errorStateService;
|
|
13425
|
+
errorContext;
|
|
13426
|
+
/**
|
|
13427
|
+
* Initialize error state for this component
|
|
13428
|
+
* Call this in ngOnInit
|
|
13429
|
+
*/
|
|
13430
|
+
initializeErrorState(service, config) {
|
|
13431
|
+
this.errorStateService = service;
|
|
13432
|
+
this.errorContext = service.createContext(config);
|
|
13433
|
+
}
|
|
13434
|
+
/**
|
|
13435
|
+
* Clean up error context
|
|
13436
|
+
* Call this in ngOnDestroy
|
|
13437
|
+
*/
|
|
13438
|
+
destroyErrorState() {
|
|
13439
|
+
if (this.errorContext) {
|
|
13440
|
+
this.errorStateService.destroyContext(this.errorContext.id);
|
|
13441
|
+
}
|
|
13442
|
+
}
|
|
13443
|
+
/**
|
|
13444
|
+
* Add an error
|
|
13445
|
+
*/
|
|
13446
|
+
addError(error) {
|
|
13447
|
+
return this.errorStateService.addError(this.errorContext.id, error);
|
|
13448
|
+
}
|
|
13449
|
+
/**
|
|
13450
|
+
* Add error from exception
|
|
13451
|
+
*/
|
|
13452
|
+
addErrorFromException(error, options) {
|
|
13453
|
+
return this.errorStateService.addFromException(this.errorContext.id, error, options);
|
|
13454
|
+
}
|
|
13455
|
+
/**
|
|
13456
|
+
* Dismiss an error
|
|
13457
|
+
*/
|
|
13458
|
+
dismissError(errorId) {
|
|
13459
|
+
this.errorStateService.dismissError(this.errorContext.id, errorId);
|
|
13460
|
+
}
|
|
13461
|
+
/**
|
|
13462
|
+
* Clear all errors
|
|
13463
|
+
*/
|
|
13464
|
+
clearErrors() {
|
|
13465
|
+
this.errorStateService.clearErrors(this.errorContext.id);
|
|
13466
|
+
}
|
|
13467
|
+
/**
|
|
13468
|
+
* Get all active errors
|
|
13469
|
+
*/
|
|
13470
|
+
get activeErrors() {
|
|
13471
|
+
return this.errorContext?.errors().filter(e => !e.dismissed) || [];
|
|
13472
|
+
}
|
|
13473
|
+
/**
|
|
13474
|
+
* Check if there are any active errors
|
|
13475
|
+
*/
|
|
13476
|
+
get hasErrors() {
|
|
13477
|
+
return this.errorContext?.hasErrors() || false;
|
|
13478
|
+
}
|
|
13479
|
+
/**
|
|
13480
|
+
* Check if there are any warnings
|
|
13481
|
+
*/
|
|
13482
|
+
get hasWarnings() {
|
|
13483
|
+
return this.errorContext?.hasWarnings() || false;
|
|
13484
|
+
}
|
|
13485
|
+
/**
|
|
13486
|
+
* Get error count
|
|
13487
|
+
*/
|
|
13488
|
+
get errorCount() {
|
|
13489
|
+
return this.errorContext?.errorCount() || 0;
|
|
13490
|
+
}
|
|
13491
|
+
}
|
|
13492
|
+
|
|
13493
|
+
/**
|
|
13494
|
+
* AMW Error Display Component
|
|
13495
|
+
*
|
|
13496
|
+
* Displays errors from an error context in a consistent, user-friendly manner.
|
|
13497
|
+
* Supports different display modes, severity icons, dismiss/retry actions.
|
|
13498
|
+
*
|
|
13499
|
+
* @example
|
|
13500
|
+
* ```html
|
|
13501
|
+
* <!-- Basic usage -->
|
|
13502
|
+
* <amw-error-display [context]="errorContext"></amw-error-display>
|
|
13503
|
+
*
|
|
13504
|
+
* <!-- With custom configuration -->
|
|
13505
|
+
* <amw-error-display
|
|
13506
|
+
* [context]="errorContext"
|
|
13507
|
+
* mode="inline"
|
|
13508
|
+
* [showDismiss]="true"
|
|
13509
|
+
* [showRetry]="true"
|
|
13510
|
+
* [maxVisible]="5">
|
|
13511
|
+
* </amw-error-display>
|
|
13512
|
+
* ```
|
|
13513
|
+
*/
|
|
13514
|
+
class AmwErrorDisplayComponent {
|
|
13515
|
+
errorStateService = inject(AmwErrorStateService);
|
|
13516
|
+
/**
|
|
13517
|
+
* The error context to display errors from
|
|
13518
|
+
*/
|
|
13519
|
+
context = input.required(...(ngDevMode ? [{ debugName: "context" }] : []));
|
|
13520
|
+
/**
|
|
13521
|
+
* Display mode
|
|
13522
|
+
* - 'banner': Full-width banner at top/bottom
|
|
13523
|
+
* - 'inline': Inline within content
|
|
13524
|
+
* - 'toast': Floating toast-style
|
|
13525
|
+
* - 'list': Simple list of errors
|
|
13526
|
+
*/
|
|
13527
|
+
mode = input('inline', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
13528
|
+
/**
|
|
13529
|
+
* Position for banner/toast modes
|
|
13530
|
+
*/
|
|
13531
|
+
position = input('top', ...(ngDevMode ? [{ debugName: "position" }] : []));
|
|
13532
|
+
/**
|
|
13533
|
+
* Whether to show dismiss buttons
|
|
13534
|
+
*/
|
|
13535
|
+
showDismiss = input(true, ...(ngDevMode ? [{ debugName: "showDismiss" }] : []));
|
|
13536
|
+
/**
|
|
13537
|
+
* Whether to show retry buttons (for errors with retry actions)
|
|
13538
|
+
*/
|
|
13539
|
+
showRetry = input(true, ...(ngDevMode ? [{ debugName: "showRetry" }] : []));
|
|
13540
|
+
/**
|
|
13541
|
+
* Maximum number of errors to show (-1 for unlimited)
|
|
13542
|
+
*/
|
|
13543
|
+
maxVisible = input(-1, ...(ngDevMode ? [{ debugName: "maxVisible" }] : []));
|
|
13544
|
+
/**
|
|
13545
|
+
* Whether to show only active (non-dismissed) errors
|
|
13546
|
+
*/
|
|
13547
|
+
activeOnly = input(true, ...(ngDevMode ? [{ debugName: "activeOnly" }] : []));
|
|
13548
|
+
/**
|
|
13549
|
+
* Filter by severity
|
|
13550
|
+
*/
|
|
13551
|
+
severityFilter = input('all', ...(ngDevMode ? [{ debugName: "severityFilter" }] : []));
|
|
13552
|
+
/**
|
|
13553
|
+
* Emitted when an error is dismissed
|
|
13554
|
+
*/
|
|
13555
|
+
errorDismissed = output();
|
|
13556
|
+
/**
|
|
13557
|
+
* Emitted when retry is clicked
|
|
13558
|
+
*/
|
|
13559
|
+
errorRetried = output();
|
|
13560
|
+
/**
|
|
13561
|
+
* Get the resolved error context
|
|
13562
|
+
*/
|
|
13563
|
+
resolvedContext = computed(() => {
|
|
13564
|
+
const ctx = this.context();
|
|
13565
|
+
if (typeof ctx === 'string') {
|
|
13566
|
+
return this.errorStateService.getContext(ctx);
|
|
13567
|
+
}
|
|
13568
|
+
return ctx;
|
|
13569
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedContext" }] : []));
|
|
13570
|
+
/**
|
|
13571
|
+
* Get filtered errors to display
|
|
13572
|
+
*/
|
|
13573
|
+
displayErrors = computed(() => {
|
|
13574
|
+
const ctx = this.resolvedContext();
|
|
13575
|
+
if (!ctx)
|
|
13576
|
+
return [];
|
|
13577
|
+
let errors = ctx.errors();
|
|
13578
|
+
// Filter active only
|
|
13579
|
+
if (this.activeOnly()) {
|
|
13580
|
+
errors = errors.filter(e => !e.dismissed);
|
|
13581
|
+
}
|
|
13582
|
+
// Filter by severity
|
|
13583
|
+
const severity = this.severityFilter();
|
|
13584
|
+
if (severity !== 'all') {
|
|
13585
|
+
errors = errors.filter(e => e.severity === severity);
|
|
13586
|
+
}
|
|
13587
|
+
// Limit visible
|
|
13588
|
+
const max = this.maxVisible();
|
|
13589
|
+
if (max > 0 && errors.length > max) {
|
|
13590
|
+
errors = errors.slice(-max);
|
|
13591
|
+
}
|
|
13592
|
+
return errors;
|
|
13593
|
+
}, ...(ngDevMode ? [{ debugName: "displayErrors" }] : []));
|
|
13594
|
+
/**
|
|
13595
|
+
* Whether there are any errors to display
|
|
13596
|
+
*/
|
|
13597
|
+
hasErrors = computed(() => this.displayErrors().length > 0, ...(ngDevMode ? [{ debugName: "hasErrors" }] : []));
|
|
13598
|
+
/**
|
|
13599
|
+
* Get hidden error count
|
|
13600
|
+
*/
|
|
13601
|
+
hiddenCount = computed(() => {
|
|
13602
|
+
const ctx = this.resolvedContext();
|
|
13603
|
+
if (!ctx)
|
|
13604
|
+
return 0;
|
|
13605
|
+
let totalErrors = ctx.errors();
|
|
13606
|
+
if (this.activeOnly()) {
|
|
13607
|
+
totalErrors = totalErrors.filter(e => !e.dismissed);
|
|
13608
|
+
}
|
|
13609
|
+
const max = this.maxVisible();
|
|
13610
|
+
if (max > 0 && totalErrors.length > max) {
|
|
13611
|
+
return totalErrors.length - max;
|
|
13612
|
+
}
|
|
13613
|
+
return 0;
|
|
13614
|
+
}, ...(ngDevMode ? [{ debugName: "hiddenCount" }] : []));
|
|
13615
|
+
/**
|
|
13616
|
+
* Get icon for severity
|
|
13617
|
+
*/
|
|
13618
|
+
getIcon(severity) {
|
|
13619
|
+
const icons = {
|
|
13620
|
+
error: 'error',
|
|
13621
|
+
warning: 'warning',
|
|
13622
|
+
info: 'info'
|
|
13623
|
+
};
|
|
13624
|
+
return icons[severity];
|
|
13625
|
+
}
|
|
13626
|
+
/**
|
|
13627
|
+
* Dismiss an error
|
|
13628
|
+
*/
|
|
13629
|
+
onDismiss(error) {
|
|
13630
|
+
const ctx = this.resolvedContext();
|
|
13631
|
+
if (ctx) {
|
|
13632
|
+
this.errorStateService.dismissError(ctx.id, error.id);
|
|
13633
|
+
this.errorDismissed.emit(error);
|
|
13634
|
+
}
|
|
13635
|
+
}
|
|
13636
|
+
/**
|
|
13637
|
+
* Retry an error's action
|
|
13638
|
+
*/
|
|
13639
|
+
onRetry(error) {
|
|
13640
|
+
const ctx = this.resolvedContext();
|
|
13641
|
+
if (ctx) {
|
|
13642
|
+
this.errorStateService.retryError(ctx.id, error.id);
|
|
13643
|
+
this.errorRetried.emit(error);
|
|
13644
|
+
}
|
|
13645
|
+
}
|
|
13646
|
+
/**
|
|
13647
|
+
* Clear all errors
|
|
13648
|
+
*/
|
|
13649
|
+
clearAll() {
|
|
13650
|
+
const ctx = this.resolvedContext();
|
|
13651
|
+
if (ctx) {
|
|
13652
|
+
this.errorStateService.clearErrors(ctx.id);
|
|
13653
|
+
}
|
|
13654
|
+
}
|
|
13655
|
+
/**
|
|
13656
|
+
* Track by function
|
|
13657
|
+
*/
|
|
13658
|
+
trackByError(index, error) {
|
|
13659
|
+
return error.id;
|
|
13660
|
+
}
|
|
13661
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwErrorDisplayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13662
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: AmwErrorDisplayComponent, isStandalone: true, selector: "amw-error-display", inputs: { context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, showDismiss: { classPropertyName: "showDismiss", publicName: "showDismiss", isSignal: true, isRequired: false, transformFunction: null }, showRetry: { classPropertyName: "showRetry", publicName: "showRetry", isSignal: true, isRequired: false, transformFunction: null }, maxVisible: { classPropertyName: "maxVisible", publicName: "maxVisible", isSignal: true, isRequired: false, transformFunction: null }, activeOnly: { classPropertyName: "activeOnly", publicName: "activeOnly", isSignal: true, isRequired: false, transformFunction: null }, severityFilter: { classPropertyName: "severityFilter", publicName: "severityFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { errorDismissed: "errorDismissed", errorRetried: "errorRetried" }, ngImport: i0, template: "@if (hasErrors()) {\n <div class=\"amw-error-display\"\n [class.amw-error-display--banner]=\"mode() === 'banner'\"\n [class.amw-error-display--inline]=\"mode() === 'inline'\"\n [class.amw-error-display--toast]=\"mode() === 'toast'\"\n [class.amw-error-display--list]=\"mode() === 'list'\"\n [class.amw-error-display--top]=\"position() === 'top'\"\n [class.amw-error-display--bottom]=\"position() === 'bottom'\">\n\n <div class=\"amw-error-display__container\">\n @for (error of displayErrors(); track trackByError($index, error)) {\n <div class=\"amw-error-display__item\"\n [class.amw-error-display__item--error]=\"error.severity === 'error'\"\n [class.amw-error-display__item--warning]=\"error.severity === 'warning'\"\n [class.amw-error-display__item--info]=\"error.severity === 'info'\"\n [class.amw-error-display__item--dismissed]=\"error.dismissed\">\n\n <div class=\"amw-error-display__icon\">\n <amw-icon [name]=\"getIcon(error.severity)\"></amw-icon>\n </div>\n\n <div class=\"amw-error-display__content\">\n <div class=\"amw-error-display__message\">{{ error.message }}</div>\n @if (error.source || error.code) {\n <div class=\"amw-error-display__meta\">\n @if (error.source) {\n <span class=\"amw-error-display__source\">{{ error.source }}</span>\n }\n @if (error.code) {\n <span class=\"amw-error-display__code\">[{{ error.code }}]</span>\n }\n </div>\n }\n </div>\n\n <div class=\"amw-error-display__actions\">\n @if (showRetry() && error.retryAction) {\n <amw-button\n appearance=\"text\"\n size=\"small\"\n icon=\"refresh\"\n (click)=\"onRetry(error)\"\n ariaLabel=\"Retry\">\n </amw-button>\n }\n @if (showDismiss() && error.dismissible) {\n <amw-button\n appearance=\"text\"\n size=\"small\"\n icon=\"close\"\n (click)=\"onDismiss(error)\"\n ariaLabel=\"Dismiss\">\n </amw-button>\n }\n </div>\n </div>\n }\n\n @if (hiddenCount() > 0) {\n <div class=\"amw-error-display__overflow\">\n +{{ hiddenCount() }} more error(s)\n </div>\n }\n\n @if (displayErrors().length > 1) {\n <div class=\"amw-error-display__footer\">\n <amw-button\n appearance=\"text\"\n size=\"small\"\n (click)=\"clearAll()\">\n Clear All\n </amw-button>\n </div>\n }\n </div>\n </div>\n}\n", styles: [".amw-error-display{font-family:var(--mdc-typography-body1-font-family, Roboto, sans-serif)}.amw-error-display--inline{margin:8px 0}.amw-error-display--banner{position:fixed;left:0;right:0;z-index:1000;padding:0 16px;box-shadow:0 2px 8px #00000026}.amw-error-display--banner.amw-error-display--top{top:0}.amw-error-display--banner.amw-error-display--bottom{bottom:0}.amw-error-display--toast{position:fixed;z-index:1000;max-width:400px;padding:8px}.amw-error-display--toast.amw-error-display--top{top:16px;right:16px}.amw-error-display--toast.amw-error-display--bottom{bottom:16px;right:16px}.amw-error-display--toast .amw-error-display__container{flex-direction:column;gap:8px}.amw-error-display--toast .amw-error-display__item{border-radius:8px;box-shadow:0 2px 8px #0003}.amw-error-display--list .amw-error-display__item{border-radius:0;border-bottom:1px solid var(--mdc-theme-outline-variant, #e0e0e0)}.amw-error-display--list .amw-error-display__item:last-child{border-bottom:none}.amw-error-display__container{display:flex;flex-direction:column;gap:4px}.amw-error-display__item{display:flex;align-items:flex-start;padding:12px 16px;border-radius:4px;animation:amw-error-slide-in .3s ease-out}.amw-error-display__item--error{background-color:var(--mdc-theme-error-container, #fdecea);color:#410002}.amw-error-display__item--error .amw-error-display__icon{color:#ba1a1a}.amw-error-display__item--warning{background-color:#fff3e0;color:#3d2800}.amw-error-display__item--warning .amw-error-display__icon{color:#9a5000}.amw-error-display__item--info{background-color:#e3f2fd;color:#002171}.amw-error-display__item--info .amw-error-display__icon{color:#002171}.amw-error-display__item--dismissed{opacity:.5;animation:amw-error-fade-out .3s ease-out}.amw-error-display__icon{flex-shrink:0;margin-right:12px;display:flex;align-items:center}.amw-error-display__icon amw-icon{font-size:20px;width:20px;height:20px}.amw-error-display__content{flex:1;min-width:0}.amw-error-display__message{font-size:14px;line-height:1.4;word-break:break-word}.amw-error-display__meta{display:flex;gap:8px;margin-top:4px;font-size:12px}.amw-error-display__source{font-weight:500}.amw-error-display__code{font-family:monospace}.amw-error-display__actions{flex-shrink:0;display:flex;gap:4px;margin-left:8px}.amw-error-display__actions amw-button{opacity:.7;transition:opacity .2s}.amw-error-display__actions amw-button:hover{opacity:1}.amw-error-display__overflow{padding:8px 16px;font-size:12px;color:var(--mdc-theme-on-surface-variant, #666);text-align:center;font-style:italic}.amw-error-display__footer{display:flex;justify-content:flex-end;padding:4px 8px}@keyframes amw-error-slide-in{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes amw-error-fade-out{0%{opacity:1}to{opacity:.5}}.dark-theme .amw-error-display__item--error{background-color:#b0002040;color:#ffb4ab}.dark-theme .amw-error-display__item--error .amw-error-display__icon{color:#ffb4ab}.dark-theme .amw-error-display__item--warning{background-color:#ff980033;color:#ffe0b2}.dark-theme .amw-error-display__item--warning .amw-error-display__icon{color:#ffe0b2}.dark-theme .amw-error-display__item--info{background-color:#2196f333;color:#bbdefb}.dark-theme .amw-error-display__item--info .amw-error-display__icon{color:#bbdefb}@media(prefers-reduced-motion:reduce){.amw-error-display__item{animation:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AmwIconComponent, selector: "amw-icon", inputs: ["name", "color", "size", "fontSet", "fontIcon", "inline", "iconClass", "ariaLabel"] }, { kind: "component", type: AmwButtonComponent, selector: "amw-button", inputs: ["type", "appearance", "fab", "icon", "iconPosition", "loading", "fullWidth", "autofocus", "text", "form", "formAction", "formMethod", "formTarget", "formEnctype", "formNoValidate", "formReset", "ripple", "disableRipple", "rippleColor", "rippleRadius", "rippleCentered", "rippleUnbounded", "spinnerSize", "spinnerColor"], outputs: ["buttonClick", "buttonFocus", "buttonBlur", "mouseenter", "mouseleave"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
13663
|
+
}
|
|
13664
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AmwErrorDisplayComponent, decorators: [{
|
|
13665
|
+
type: Component,
|
|
13666
|
+
args: [{ selector: 'amw-error-display', standalone: true, imports: [CommonModule, AmwIconComponent, AmwButtonComponent], encapsulation: ViewEncapsulation.None, template: "@if (hasErrors()) {\n <div class=\"amw-error-display\"\n [class.amw-error-display--banner]=\"mode() === 'banner'\"\n [class.amw-error-display--inline]=\"mode() === 'inline'\"\n [class.amw-error-display--toast]=\"mode() === 'toast'\"\n [class.amw-error-display--list]=\"mode() === 'list'\"\n [class.amw-error-display--top]=\"position() === 'top'\"\n [class.amw-error-display--bottom]=\"position() === 'bottom'\">\n\n <div class=\"amw-error-display__container\">\n @for (error of displayErrors(); track trackByError($index, error)) {\n <div class=\"amw-error-display__item\"\n [class.amw-error-display__item--error]=\"error.severity === 'error'\"\n [class.amw-error-display__item--warning]=\"error.severity === 'warning'\"\n [class.amw-error-display__item--info]=\"error.severity === 'info'\"\n [class.amw-error-display__item--dismissed]=\"error.dismissed\">\n\n <div class=\"amw-error-display__icon\">\n <amw-icon [name]=\"getIcon(error.severity)\"></amw-icon>\n </div>\n\n <div class=\"amw-error-display__content\">\n <div class=\"amw-error-display__message\">{{ error.message }}</div>\n @if (error.source || error.code) {\n <div class=\"amw-error-display__meta\">\n @if (error.source) {\n <span class=\"amw-error-display__source\">{{ error.source }}</span>\n }\n @if (error.code) {\n <span class=\"amw-error-display__code\">[{{ error.code }}]</span>\n }\n </div>\n }\n </div>\n\n <div class=\"amw-error-display__actions\">\n @if (showRetry() && error.retryAction) {\n <amw-button\n appearance=\"text\"\n size=\"small\"\n icon=\"refresh\"\n (click)=\"onRetry(error)\"\n ariaLabel=\"Retry\">\n </amw-button>\n }\n @if (showDismiss() && error.dismissible) {\n <amw-button\n appearance=\"text\"\n size=\"small\"\n icon=\"close\"\n (click)=\"onDismiss(error)\"\n ariaLabel=\"Dismiss\">\n </amw-button>\n }\n </div>\n </div>\n }\n\n @if (hiddenCount() > 0) {\n <div class=\"amw-error-display__overflow\">\n +{{ hiddenCount() }} more error(s)\n </div>\n }\n\n @if (displayErrors().length > 1) {\n <div class=\"amw-error-display__footer\">\n <amw-button\n appearance=\"text\"\n size=\"small\"\n (click)=\"clearAll()\">\n Clear All\n </amw-button>\n </div>\n }\n </div>\n </div>\n}\n", styles: [".amw-error-display{font-family:var(--mdc-typography-body1-font-family, Roboto, sans-serif)}.amw-error-display--inline{margin:8px 0}.amw-error-display--banner{position:fixed;left:0;right:0;z-index:1000;padding:0 16px;box-shadow:0 2px 8px #00000026}.amw-error-display--banner.amw-error-display--top{top:0}.amw-error-display--banner.amw-error-display--bottom{bottom:0}.amw-error-display--toast{position:fixed;z-index:1000;max-width:400px;padding:8px}.amw-error-display--toast.amw-error-display--top{top:16px;right:16px}.amw-error-display--toast.amw-error-display--bottom{bottom:16px;right:16px}.amw-error-display--toast .amw-error-display__container{flex-direction:column;gap:8px}.amw-error-display--toast .amw-error-display__item{border-radius:8px;box-shadow:0 2px 8px #0003}.amw-error-display--list .amw-error-display__item{border-radius:0;border-bottom:1px solid var(--mdc-theme-outline-variant, #e0e0e0)}.amw-error-display--list .amw-error-display__item:last-child{border-bottom:none}.amw-error-display__container{display:flex;flex-direction:column;gap:4px}.amw-error-display__item{display:flex;align-items:flex-start;padding:12px 16px;border-radius:4px;animation:amw-error-slide-in .3s ease-out}.amw-error-display__item--error{background-color:var(--mdc-theme-error-container, #fdecea);color:#410002}.amw-error-display__item--error .amw-error-display__icon{color:#ba1a1a}.amw-error-display__item--warning{background-color:#fff3e0;color:#3d2800}.amw-error-display__item--warning .amw-error-display__icon{color:#9a5000}.amw-error-display__item--info{background-color:#e3f2fd;color:#002171}.amw-error-display__item--info .amw-error-display__icon{color:#002171}.amw-error-display__item--dismissed{opacity:.5;animation:amw-error-fade-out .3s ease-out}.amw-error-display__icon{flex-shrink:0;margin-right:12px;display:flex;align-items:center}.amw-error-display__icon amw-icon{font-size:20px;width:20px;height:20px}.amw-error-display__content{flex:1;min-width:0}.amw-error-display__message{font-size:14px;line-height:1.4;word-break:break-word}.amw-error-display__meta{display:flex;gap:8px;margin-top:4px;font-size:12px}.amw-error-display__source{font-weight:500}.amw-error-display__code{font-family:monospace}.amw-error-display__actions{flex-shrink:0;display:flex;gap:4px;margin-left:8px}.amw-error-display__actions amw-button{opacity:.7;transition:opacity .2s}.amw-error-display__actions amw-button:hover{opacity:1}.amw-error-display__overflow{padding:8px 16px;font-size:12px;color:var(--mdc-theme-on-surface-variant, #666);text-align:center;font-style:italic}.amw-error-display__footer{display:flex;justify-content:flex-end;padding:4px 8px}@keyframes amw-error-slide-in{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes amw-error-fade-out{0%{opacity:1}to{opacity:.5}}.dark-theme .amw-error-display__item--error{background-color:#b0002040;color:#ffb4ab}.dark-theme .amw-error-display__item--error .amw-error-display__icon{color:#ffb4ab}.dark-theme .amw-error-display__item--warning{background-color:#ff980033;color:#ffe0b2}.dark-theme .amw-error-display__item--warning .amw-error-display__icon{color:#ffe0b2}.dark-theme .amw-error-display__item--info{background-color:#2196f333;color:#bbdefb}.dark-theme .amw-error-display__item--info .amw-error-display__icon{color:#bbdefb}@media(prefers-reduced-motion:reduce){.amw-error-display__item{animation:none}}\n"] }]
|
|
13667
|
+
}], propDecorators: { context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: true }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], showDismiss: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDismiss", required: false }] }], showRetry: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRetry", required: false }] }], maxVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxVisible", required: false }] }], activeOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeOnly", required: false }] }], severityFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "severityFilter", required: false }] }], errorDismissed: [{ type: i0.Output, args: ["errorDismissed"] }], errorRetried: [{ type: i0.Output, args: ["errorRetried"] }] } });
|
|
13668
|
+
|
|
12180
13669
|
// Calendar components
|
|
12181
13670
|
|
|
12182
13671
|
/**
|
|
@@ -17685,5 +19174,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
17685
19174
|
* Generated bundle index. Do not edit.
|
|
17686
19175
|
*/
|
|
17687
19176
|
|
|
17688
|
-
export { AmwAccordionComponent, AmwAccordionPanelComponent, AmwAutoFocusDirective, AmwAutocompleteComponent, AmwBadgeDirective, AmwButtonComponent, AmwButtonToggleComponent, AmwButtonToggleGroupComponent, AmwCacheConfigService, AmwCacheSyncService, AmwCalendarBaseComponent, AmwCalendarFullComponent, AmwCalendarMiniComponent, AmwCalendarPickerComponent, AmwCardComponent, AmwCardService, AmwCheckboxComponent, AmwChipComponent, AmwChipInputComponent, AmwChipsComponent, AmwClickOutsideDirective, AmwColorPickerComponent, AmwCopyToClipboardDirective, AmwCurrencyPipe, AmwDashboardPageComponent, AmwDataTableComponent, AmwDatePipe, AmwDatepickerComponent, AmwDetailPageComponent, AmwDialogComponent, AmwDialogRef, AmwDialogService, AmwDividerComponent, AmwFileInputComponent, AmwFormPageComponent, AmwFormValidationComponent, AmwHttpCacheService, AmwIconButtonComponent, AmwIconComponent, AmwIndexedDbStorageService, AmwInputComponent, AmwListComponent, AmwListItemComponent, AmwListPageComponent, AmwLoadingService, AmwMasterDetailPageComponent, AmwMenuComponent, AmwMenuItemComponent, AmwMenuTriggerForDirective, AmwMessagingService, AmwNotificationService, AmwPaginatorComponent, AmwPopoverComponent, AmwProgressBarComponent, AmwProgressSpinnerComponent, AmwRadioComponent, AmwRadioGroupComponent, AmwRangeSliderComponent, AmwReportPageComponent, AmwSearchPageComponent, AmwSelectComponent, AmwSidenavComponent, AmwSliderComponent, AmwStepperComponent, AmwSwitchComponent, AmwTabComponent, AmwTabsComponent, AmwTextTransformPipe, AmwTextareaComponent, AmwThemeEditorComponent, AmwThemeManagerComponent, AmwThemePickerComponent, AmwThemeService, AmwTimepickerComponent, AmwToggleComponent, AmwToolbarComponent, AmwTooltipComponent, AmwTooltipDirective, AmwWorkflowPageComponent, BaseComponent, DefaultDashboardDataSource, DefaultFormPageDataSource, DefaultMasterDetailDataSource, DefaultReportPageDataSource, DefaultWorkflowPageDataSource, HttpCacheInterceptor, LIST_PAGE_DATA_SOURCE, WORKFLOW_PAGE_DATA_SOURCE, getAmwDialogTitle, httpCacheInterceptor };
|
|
19177
|
+
export { AmwAccordionComponent, AmwAccordionPanelComponent, AmwAutoFocusDirective, AmwAutocompleteComponent, AmwBadgeDirective, AmwButtonComponent, AmwButtonToggleComponent, AmwButtonToggleGroupComponent, AmwCacheConfigService, AmwCacheSyncService, AmwCalendarBaseComponent, AmwCalendarFullComponent, AmwCalendarMiniComponent, AmwCalendarPickerComponent, AmwCardComponent, AmwCardService, AmwCheckboxComponent, AmwChipComponent, AmwChipInputComponent, AmwChipsComponent, AmwClickOutsideDirective, AmwColorPickerComponent, AmwCopyToClipboardDirective, AmwCurrencyPipe, AmwDashboardPageComponent, AmwDataTableComponent, AmwDatePipe, AmwDatepickerComponent, AmwDetailPageComponent, AmwDialogComponent, AmwDialogRef, AmwDialogService, AmwDividerComponent, AmwErrorDisplayComponent, AmwErrorStateService, AmwFileInputComponent, AmwFormPageComponent, AmwFormValidationComponent, AmwFullScreenLoadingComponent, AmwFullScreenLoadingService, AmwHttpCacheService, AmwIconButtonComponent, AmwIconComponent, AmwIndexedDbStorageService, AmwInputComponent, AmwListComponent, AmwListItemComponent, AmwListPageComponent, AmwLoadingService, AmwMasterDetailPageComponent, AmwMenuComponent, AmwMenuItemComponent, AmwMenuTriggerForDirective, AmwMessagingService, AmwNotificationService, AmwPaginatorComponent, AmwPopoverComponent, AmwProgressBarComponent, AmwProgressSpinnerComponent, AmwRadioComponent, AmwRadioGroupComponent, AmwRangeSliderComponent, AmwReportPageComponent, AmwSearchPageComponent, AmwSelectComponent, AmwSidenavComponent, AmwSliderComponent, AmwStepperComponent, AmwSwitchComponent, AmwTabComponent, AmwTabsComponent, AmwTextTransformPipe, AmwTextareaComponent, AmwThemeEditorComponent, AmwThemeManagerComponent, AmwThemePickerComponent, AmwThemeService, AmwTimepickerComponent, AmwToggleComponent, AmwToolbarComponent, AmwTooltipComponent, AmwTooltipDirective, AmwValidationService, AmwValidationTooltipDirective, AmwWorkflowPageComponent, BaseComponent, DefaultDashboardDataSource, DefaultFormPageDataSource, DefaultMasterDetailDataSource, DefaultReportPageDataSource, DefaultWorkflowPageDataSource, ErrorStateMixin, HttpCacheInterceptor, LIST_PAGE_DATA_SOURCE, ValidationMixin, WORKFLOW_PAGE_DATA_SOURCE, getAmwDialogTitle, httpCacheInterceptor, loading };
|
|
17689
19178
|
//# sourceMappingURL=angular-material-wrap.mjs.map
|