integra-ng 21.0.17 → 21.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/integra-ng.mjs +156 -7
- package/fesm2022/integra-ng.mjs.map +1 -1
- package/package.json +1 -1
- package/types/integra-ng.d.ts +74 -5
package/fesm2022/integra-ng.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { EventEmitter, Output, Input, Component, ContentChildren, ElementRef, ViewChild, HostBinding, createComponent, Directive, Optional, Self, forwardRef, HostListener, ChangeDetectionStrategy, signal, inject, EnvironmentInjector, ApplicationRef, Injectable, effect, computed, input, ViewEncapsulation, ChangeDetectorRef, ViewChildren, Inject } from '@angular/core';
|
|
2
|
+
import { EventEmitter, Output, Input, Component, ContentChildren, ElementRef, ViewChild, HostBinding, createComponent, Directive, Optional, Self, forwardRef, HostListener, ChangeDetectionStrategy, signal, inject, EnvironmentInjector, ApplicationRef, Injectable, effect, InjectionToken, computed, input, ViewEncapsulation, ChangeDetectorRef, ViewChildren, Inject } from '@angular/core';
|
|
3
3
|
import * as i1$1 from '@angular/common';
|
|
4
4
|
import { NgClass, CommonModule, NgTemplateOutlet, NgStyle, DOCUMENT } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
6
|
import { FormsModule, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
|
|
7
|
-
import { take } from 'rxjs/operators';
|
|
8
|
-
import { Subject,
|
|
7
|
+
import { take, takeUntil, switchMap, map } from 'rxjs/operators';
|
|
8
|
+
import { Subject, BehaviorSubject, of, forkJoin, filter } from 'rxjs';
|
|
9
9
|
import * as i2 from '@angular/router';
|
|
10
10
|
import { RouterLink, RouterLinkActive, NavigationEnd, RouterOutlet } from '@angular/router';
|
|
11
11
|
import { trigger, state, transition, style, animate } from '@angular/animations';
|
|
@@ -3159,14 +3159,163 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
3159
3159
|
type: Input
|
|
3160
3160
|
}] } });
|
|
3161
3161
|
|
|
3162
|
+
/**
|
|
3163
|
+
* Injection token for providing a claims checking service.
|
|
3164
|
+
* Applications using claims-based menu filtering should provide
|
|
3165
|
+
* an implementation of ClaimsChecker using this token.
|
|
3166
|
+
*
|
|
3167
|
+
* @example
|
|
3168
|
+
* // In your app.config.ts or module:
|
|
3169
|
+
* providers: [
|
|
3170
|
+
* {
|
|
3171
|
+
* provide: CLAIMS_CHECKER,
|
|
3172
|
+
* useExisting: ClaimsClient // Your claims service
|
|
3173
|
+
* }
|
|
3174
|
+
* ]
|
|
3175
|
+
*/
|
|
3176
|
+
const CLAIMS_CHECKER = new InjectionToken('ClaimsChecker');
|
|
3177
|
+
/**
|
|
3178
|
+
* Provides a claims checker service for menu item filtering.
|
|
3179
|
+
* This is the recommended way to configure claims-based access control in your application.
|
|
3180
|
+
*
|
|
3181
|
+
* @param claimsService The claims service instance that implements the ClaimsChecker interface
|
|
3182
|
+
* @returns Provider configuration for dependency injection
|
|
3183
|
+
*
|
|
3184
|
+
* @example
|
|
3185
|
+
* // In your app.config.ts:
|
|
3186
|
+
* import { provideMenuClaimsChecker } from 'integra-ng';
|
|
3187
|
+
* import { ClaimsService } from './services/claims.service';
|
|
3188
|
+
*
|
|
3189
|
+
* export const appConfig: ApplicationConfig = {
|
|
3190
|
+
* providers: [
|
|
3191
|
+
* provideMenuClaimsChecker(ClaimsService),
|
|
3192
|
+
* // ... other providers
|
|
3193
|
+
* ]
|
|
3194
|
+
* };
|
|
3195
|
+
*
|
|
3196
|
+
* @example
|
|
3197
|
+
* // Or provide an existing service instance:
|
|
3198
|
+
* providers: [
|
|
3199
|
+
* ClaimsService,
|
|
3200
|
+
* provideMenuClaimsChecker(ClaimsService)
|
|
3201
|
+
* ]
|
|
3202
|
+
*/
|
|
3203
|
+
function provideMenuClaimsChecker(claimsService) {
|
|
3204
|
+
return {
|
|
3205
|
+
provide: CLAIMS_CHECKER,
|
|
3206
|
+
useExisting: claimsService,
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3162
3210
|
class MenuComponent {
|
|
3163
|
-
|
|
3211
|
+
claimsChecker = inject(CLAIMS_CHECKER, { optional: true });
|
|
3212
|
+
destroy$ = new Subject();
|
|
3213
|
+
modelSubject$ = new BehaviorSubject([]);
|
|
3214
|
+
filteredModel = signal([], ...(ngDevMode ? [{ debugName: "filteredModel" }] : []));
|
|
3215
|
+
set model(value) {
|
|
3216
|
+
this.modelSubject$.next(value);
|
|
3217
|
+
}
|
|
3218
|
+
ngOnInit() {
|
|
3219
|
+
this.modelSubject$
|
|
3220
|
+
.pipe(takeUntil(this.destroy$), switchMap((model) => this.filterModelByClaims(model)))
|
|
3221
|
+
.subscribe((filteredModel) => {
|
|
3222
|
+
this.filteredModel.set(filteredModel);
|
|
3223
|
+
});
|
|
3224
|
+
}
|
|
3225
|
+
ngOnDestroy() {
|
|
3226
|
+
this.destroy$.next();
|
|
3227
|
+
this.destroy$.complete();
|
|
3228
|
+
}
|
|
3229
|
+
filterModelByClaims(model) {
|
|
3230
|
+
// If no claims checker is provided, return all menu items (backward compatible)
|
|
3231
|
+
if (!this.claimsChecker) {
|
|
3232
|
+
return of(model);
|
|
3233
|
+
}
|
|
3234
|
+
// Collect all unique claims from the model
|
|
3235
|
+
const claims = this.collectClaims(model);
|
|
3236
|
+
if (claims.size === 0) {
|
|
3237
|
+
return of(model);
|
|
3238
|
+
}
|
|
3239
|
+
// Check all claims in parallel using forkJoin
|
|
3240
|
+
const claimChecks = {};
|
|
3241
|
+
claims.forEach((claim) => {
|
|
3242
|
+
claimChecks[claim] = this.claimsChecker.hasClaim(claim);
|
|
3243
|
+
});
|
|
3244
|
+
if (Object.keys(claimChecks).length === 0) {
|
|
3245
|
+
return of(model);
|
|
3246
|
+
}
|
|
3247
|
+
return forkJoin(claimChecks).pipe(map((claimsMap) => this.filterModel(model, claimsMap)));
|
|
3248
|
+
}
|
|
3249
|
+
collectClaims(model) {
|
|
3250
|
+
const claims = new Set();
|
|
3251
|
+
model.forEach((group) => {
|
|
3252
|
+
if (group.claim) {
|
|
3253
|
+
claims.add(group.claim);
|
|
3254
|
+
}
|
|
3255
|
+
group.items.forEach((item) => {
|
|
3256
|
+
this.collectItemClaims(item, claims);
|
|
3257
|
+
});
|
|
3258
|
+
});
|
|
3259
|
+
return claims;
|
|
3260
|
+
}
|
|
3261
|
+
collectItemClaims(item, claims) {
|
|
3262
|
+
if (item.claim) {
|
|
3263
|
+
claims.add(item.claim);
|
|
3264
|
+
}
|
|
3265
|
+
if (item.items) {
|
|
3266
|
+
item.items.forEach((subItem) => {
|
|
3267
|
+
this.collectItemClaims(subItem, claims);
|
|
3268
|
+
});
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
filterModel(model, claimsMap) {
|
|
3272
|
+
return model
|
|
3273
|
+
.filter((group) => {
|
|
3274
|
+
// If group has a claim, check if user has access
|
|
3275
|
+
if (group.claim && !claimsMap[group.claim]) {
|
|
3276
|
+
return false;
|
|
3277
|
+
}
|
|
3278
|
+
return true;
|
|
3279
|
+
})
|
|
3280
|
+
.map((group) => ({
|
|
3281
|
+
...group,
|
|
3282
|
+
items: this.filterItems(group.items, claimsMap),
|
|
3283
|
+
}))
|
|
3284
|
+
.filter((group) => group.items.length > 0); // Remove groups with no visible items
|
|
3285
|
+
}
|
|
3286
|
+
filterItems(items, claimsMap) {
|
|
3287
|
+
return items
|
|
3288
|
+
.filter((item) => {
|
|
3289
|
+
// If item has a claim, check if user has access
|
|
3290
|
+
if (item.claim && !claimsMap[item.claim]) {
|
|
3291
|
+
return false;
|
|
3292
|
+
}
|
|
3293
|
+
return true;
|
|
3294
|
+
})
|
|
3295
|
+
.map((item) => {
|
|
3296
|
+
// If item has nested items, filter them recursively
|
|
3297
|
+
if (item.items) {
|
|
3298
|
+
return {
|
|
3299
|
+
...item,
|
|
3300
|
+
items: this.filterItems(item.items, claimsMap),
|
|
3301
|
+
};
|
|
3302
|
+
}
|
|
3303
|
+
return item;
|
|
3304
|
+
})
|
|
3305
|
+
.filter((item) => {
|
|
3306
|
+
// If item has nested items, keep it only if it has visible nested items
|
|
3307
|
+
if (item.items) {
|
|
3308
|
+
return item.items.length > 0;
|
|
3309
|
+
}
|
|
3310
|
+
return true;
|
|
3311
|
+
});
|
|
3312
|
+
}
|
|
3164
3313
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: MenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3165
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: MenuComponent, isStandalone: true, selector: "i-menu", inputs: { model: "model" }, ngImport: i0, template: "<ul class=\"menu\">\n @for(group of
|
|
3314
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: MenuComponent, isStandalone: true, selector: "i-menu", inputs: { model: "model" }, ngImport: i0, template: "<ul class=\"menu\">\n @for(group of filteredModel(); track $index) {\n <li class=\"menu-group\">\n @if (group.label) {\n <div class=\"menu-group-label\">\n {{ group.label }}\n </div>\n }\n\n <ul class=\"menu-items\">\n @for(item of group.items; track $index) {\n <li class=\"menu-item\">\n <a\n [routerLink]=\"item.routerLink\"\n routerLinkActive=\"active\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n @if (item.icon) {\n <i class=\"{{ item.icon }}\"></i>\n }\n <span>{{ item.label }}</span>\n </a>\n\n @if (item.items) {\n <ul class=\"submenu\">\n @for(subItem of item.items; track $index) {\n <li class=\"submenu-item\">\n <a\n [routerLink]=\"subItem.routerLink\"\n routerLinkActive=\"active\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n @if (subItem.icon) {\n <i class=\"{{ subItem.icon }}\"></i>\n }\n <span>{{ subItem.label }}</span>\n </a>\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n\n @if (group.separator) {\n <hr class=\"separator\" />\n }\n </li>\n }\n</ul>\n", styles: [".menu{list-style:none;margin:0;padding:0}.menu-group{margin-bottom:1rem}.menu-group .menu-group-label{font-size:1em;font-weight:600;text-transform:uppercase;padding:.5rem 1rem;color:var(--color-contrast)}.menu-group .menu-items{list-style:none;margin:0;padding:0}.menu-group .menu-items .menu-item a{display:flex;align-items:center;padding:.75rem 1rem;color:var(--color-text-secondary);text-decoration:none;font-size:1em;border-left:3px solid transparent;transition:all .2s ease}.menu-group .menu-items .menu-item a i{margin-right:.5rem;font-size:1em}.menu-group .menu-items .menu-item a:hover{background-color:var(--surface-hover);border-radius:6px}.menu-group .menu-items .menu-item a.active{background-color:var(--surface-hover);border-radius:6px;border-left:3px solid var(--color-primary);font-weight:600}.menu-group .menu-items .menu-item .submenu{list-style:none;padding-left:1.5rem}.menu-group .menu-items .menu-item .submenu .submenu-item a{padding:.5rem 1rem;font-size:.95em;color:var(--color-text-tertiary)}.menu-group .menu-items .menu-item .submenu .submenu-item a:hover{background-color:var(--surface-hover);color:var(--color-text-primary)}.menu-group .menu-items .menu-item .submenu .submenu-item a.active{color:var(--color-primary);font-weight:600}.separator{border:none;border-top:1px solid #334155;margin:.5rem 0}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }] });
|
|
3166
3315
|
}
|
|
3167
3316
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: MenuComponent, decorators: [{
|
|
3168
3317
|
type: Component,
|
|
3169
|
-
args: [{ selector: 'i-menu', imports: [RouterLink, RouterLinkActive], template: "<ul class=\"menu\">\n @for(group of
|
|
3318
|
+
args: [{ selector: 'i-menu', imports: [RouterLink, RouterLinkActive], template: "<ul class=\"menu\">\n @for(group of filteredModel(); track $index) {\n <li class=\"menu-group\">\n @if (group.label) {\n <div class=\"menu-group-label\">\n {{ group.label }}\n </div>\n }\n\n <ul class=\"menu-items\">\n @for(item of group.items; track $index) {\n <li class=\"menu-item\">\n <a\n [routerLink]=\"item.routerLink\"\n routerLinkActive=\"active\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n @if (item.icon) {\n <i class=\"{{ item.icon }}\"></i>\n }\n <span>{{ item.label }}</span>\n </a>\n\n @if (item.items) {\n <ul class=\"submenu\">\n @for(subItem of item.items; track $index) {\n <li class=\"submenu-item\">\n <a\n [routerLink]=\"subItem.routerLink\"\n routerLinkActive=\"active\"\n [routerLinkActiveOptions]=\"{ exact: true }\"\n >\n @if (subItem.icon) {\n <i class=\"{{ subItem.icon }}\"></i>\n }\n <span>{{ subItem.label }}</span>\n </a>\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n\n @if (group.separator) {\n <hr class=\"separator\" />\n }\n </li>\n }\n</ul>\n", styles: [".menu{list-style:none;margin:0;padding:0}.menu-group{margin-bottom:1rem}.menu-group .menu-group-label{font-size:1em;font-weight:600;text-transform:uppercase;padding:.5rem 1rem;color:var(--color-contrast)}.menu-group .menu-items{list-style:none;margin:0;padding:0}.menu-group .menu-items .menu-item a{display:flex;align-items:center;padding:.75rem 1rem;color:var(--color-text-secondary);text-decoration:none;font-size:1em;border-left:3px solid transparent;transition:all .2s ease}.menu-group .menu-items .menu-item a i{margin-right:.5rem;font-size:1em}.menu-group .menu-items .menu-item a:hover{background-color:var(--surface-hover);border-radius:6px}.menu-group .menu-items .menu-item a.active{background-color:var(--surface-hover);border-radius:6px;border-left:3px solid var(--color-primary);font-weight:600}.menu-group .menu-items .menu-item .submenu{list-style:none;padding-left:1.5rem}.menu-group .menu-items .menu-item .submenu .submenu-item a{padding:.5rem 1rem;font-size:.95em;color:var(--color-text-tertiary)}.menu-group .menu-items .menu-item .submenu .submenu-item a:hover{background-color:var(--surface-hover);color:var(--color-text-primary)}.menu-group .menu-items .menu-item .submenu .submenu-item a.active{color:var(--color-primary);font-weight:600}.separator{border:none;border-top:1px solid #334155;margin:.5rem 0}\n"] }]
|
|
3170
3319
|
}], propDecorators: { model: [{
|
|
3171
3320
|
type: Input
|
|
3172
3321
|
}] } });
|
|
@@ -9137,5 +9286,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
9137
9286
|
* Generated bundle index. Do not edit.
|
|
9138
9287
|
*/
|
|
9139
9288
|
|
|
9140
|
-
export { AbstractDialog, ConfirmationDialogComponent, ConfirmationDialogService, DataUpdateEventService, DialogService, EmptyStateComponent, IAccordion, IAccordionList, IButton, ICalendar, ICard, IChart, ICheckbox, IChip, IChipsComponent, IDialog, IDialogActions, IDialogBase, IInputText, IListbox, IMessage, IMultiSelect, IOverlayPanel, IPanel, IPlaceholder, IProgressSpinner, IRadioButton, ISelect, ITabPanel, ITable, ITabs, ITreeView, IWhisper, LayoutComponent, LayoutService, LocalStorageColorSchemeKey, MenuComponent, NoContentComponent, SeoService, SidebarComponent, StructuredDataService, TooltipComponent, TooltipDirective, TopbarComponent, UniqueComponentId, WhisperService, ZIndexUtils, lastId };
|
|
9289
|
+
export { AbstractDialog, CLAIMS_CHECKER, ConfirmationDialogComponent, ConfirmationDialogService, DataUpdateEventService, DialogService, EmptyStateComponent, IAccordion, IAccordionList, IButton, ICalendar, ICard, IChart, ICheckbox, IChip, IChipsComponent, IDialog, IDialogActions, IDialogBase, IInputText, IListbox, IMessage, IMultiSelect, IOverlayPanel, IPanel, IPlaceholder, IProgressSpinner, IRadioButton, ISelect, ITabPanel, ITable, ITabs, ITreeView, IWhisper, LayoutComponent, LayoutService, LocalStorageColorSchemeKey, MenuComponent, NoContentComponent, SeoService, SidebarComponent, StructuredDataService, TooltipComponent, TooltipDirective, TopbarComponent, UniqueComponentId, WhisperService, ZIndexUtils, lastId, provideMenuClaimsChecker };
|
|
9141
9290
|
//# sourceMappingURL=integra-ng.mjs.map
|