@sinequa/atomic-angular 1.0.10 → 1.0.12
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/sinequa-atomic-angular.mjs +263 -164
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +66 -26
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, HostBinding, Component, Pipe, InjectionToken, computed, ChangeDetectorRef, DestroyRef, LOCALE_ID, Inject, Optional, input, output, signal, effect, assertInInjectionContext, runInInjectionContext, Injector, EventEmitter, Directive, viewChild, ElementRef, afterNextRender, untracked, linkedSignal, model, TemplateRef, HostListener, Renderer2, contentChildren, contentChild, booleanAttribute, ChangeDetectionStrategy, resource, ViewContainerRef, viewChildren, numberAttribute, afterEveryRender } from '@angular/core';
|
|
2
|
+
import { Injectable, inject, HostBinding, Component, Pipe, InjectionToken, computed, ChangeDetectorRef, DestroyRef, LOCALE_ID, Inject, Optional, input, output, signal, effect, assertInInjectionContext, runInInjectionContext, Injector, EventEmitter, Directive, viewChild, ElementRef, afterNextRender, untracked, linkedSignal, model, TemplateRef, HostListener, Renderer2, contentChildren, contentChild, booleanAttribute, ChangeDetectionStrategy, resource, ViewContainerRef, viewChildren, numberAttribute, afterRenderEffect, afterEveryRender } from '@angular/core';
|
|
3
3
|
import { BehaviorSubject, Subscription, catchError, EMPTY, firstValueFrom, map, Subject, of, tap, throwError, filter, shareReplay, fromEvent, debounceTime, from, switchMap } from 'rxjs';
|
|
4
4
|
import { TranslocoService, TranslocoPipe, provideTranslocoScope } from '@jsverse/transloco';
|
|
5
5
|
import { DropdownComponent, DropdownContentComponent, InputComponent, ButtonComponent, cn, FaIconComponent, EllipsisIcon, ChevronRightIcon, MenuComponent, MenuContentComponent, MenuItemComponent, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogTitleComponent, DialogContentComponent, DialogFooterComponent, ListItemComponent, SwitchComponent, SelectOptionDirective, DialogService, TabsComponent, TabsListComponent, TabComponent, ChevronLeftIconComponent, ChevronsLeftIconComponent, ChevronsRightIconComponent, Separator, SheetCloseDirective, SheetService, DateRangePickerDirective, DatepickerDirective, ButtonGroup, InputGroupInput, InputGroupComponent, InputGroupAddonComponent, SearchIcon, FilterIcon, LoadingCircleIconComponent, CircleCheckIconComponent, PopoverComponent, CardComponent, CardHeaderComponent, CardContentComponent, CardFooterComponent, BookmarkIcon, PopoverContentComponent, UserIcon, TrashIcon, FolderIcon, VerticalDividerComponent, BreakpointObserverService, HorizontalDividerComponent, FlagEnglishIconComponent, FlagFrenchIconComponent, EditIcon, UndoIcon, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent } from '@sinequa/ui';
|
|
@@ -3415,14 +3415,10 @@ function AuthGuard() {
|
|
|
3415
3415
|
const { loginPath, useCredentials, useSSO } = globalConfig;
|
|
3416
3416
|
if (state.url.startsWith("/login"))
|
|
3417
3417
|
return true;
|
|
3418
|
-
// If the user is not authenticated, navigate to the login page
|
|
3418
|
+
// If the user is not authenticated, navigate to the login page.
|
|
3419
|
+
// The login page handles every authentication method (credentials, OAuth, SAML).
|
|
3419
3420
|
if (!isAuthenticated() && !useSSO) {
|
|
3420
|
-
|
|
3421
|
-
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3422
|
-
}
|
|
3423
|
-
else {
|
|
3424
|
-
router.navigate(["loading"], { queryParams: { returnUrl: state.url } });
|
|
3425
|
-
}
|
|
3421
|
+
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3426
3422
|
return false;
|
|
3427
3423
|
}
|
|
3428
3424
|
// If the user is authenticated, initialize the principal store if it's in the initial state
|
|
@@ -4031,7 +4027,7 @@ class NavigationService {
|
|
|
4031
4027
|
* - Maps all router events to `RouterEvent`.
|
|
4032
4028
|
* - Filters the events to only include instances of `NavigationEnd`.
|
|
4033
4029
|
* - Taps into the event stream to extract the route name from the URL and notify the audit service of route changes,
|
|
4034
|
-
* excluding
|
|
4030
|
+
* excluding duplicate navigations.
|
|
4035
4031
|
* - Updates the `urlAfterNavigation` property with the current URL after navigation.
|
|
4036
4032
|
* - Shares the replayed value with a buffer size of 1 to ensure subscribers receive the latest emitted value.
|
|
4037
4033
|
*
|
|
@@ -4039,8 +4035,7 @@ class NavigationService {
|
|
|
4039
4035
|
*/
|
|
4040
4036
|
navigationEnd$ = this.router.events.pipe(map((event) => event), filter((event) => event instanceof NavigationEnd), tap((event) => {
|
|
4041
4037
|
const url = event.url.slice(1).split("?")[0]; // Extract route name
|
|
4042
|
-
|
|
4043
|
-
if (url && url !== "loading" && url !== this.urlAfterNavigation) {
|
|
4038
|
+
if (url && url !== this.urlAfterNavigation) {
|
|
4044
4039
|
this.auditService.notifyRouteChange(url);
|
|
4045
4040
|
}
|
|
4046
4041
|
}), tap((event) => {
|
|
@@ -5372,6 +5367,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
5372
5367
|
}]
|
|
5373
5368
|
}] });
|
|
5374
5369
|
|
|
5370
|
+
/**
|
|
5371
|
+
* @deprecated This component and its `/loading` route are no longer used.
|
|
5372
|
+
* Authentication is handled at bootstrap by `withBootstrapApp`/`signIn`, and the
|
|
5373
|
+
* `AuthGuard` now redirects unauthenticated users to the login page directly.
|
|
5374
|
+
* Will be removed in a future major release. Do not use in new code.
|
|
5375
|
+
*/
|
|
5375
5376
|
class LoadingComponent {
|
|
5376
5377
|
state = computed(() => getState(this.application), ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
5377
5378
|
application = inject(ApplicationStore);
|
|
@@ -6925,8 +6926,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
6925
6926
|
}]
|
|
6926
6927
|
}] });
|
|
6927
6928
|
/**
|
|
6928
|
-
* Directive to be used on the
|
|
6929
|
-
*
|
|
6929
|
+
* Directive to be used on the "more" trigger element. The overflow manager
|
|
6930
|
+
* reserves this element's size when not all items fit, so the last visible
|
|
6931
|
+
* item never overlaps it. Its position is not used for measurement.
|
|
6930
6932
|
*/
|
|
6931
6933
|
class OverflowStopDirective {
|
|
6932
6934
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: OverflowStopDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -6940,10 +6942,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
6940
6942
|
}]
|
|
6941
6943
|
}] });
|
|
6942
6944
|
/**
|
|
6943
|
-
* Directive that
|
|
6944
|
-
*
|
|
6945
|
-
*
|
|
6946
|
-
*
|
|
6945
|
+
* Directive that counts how many items fit inside the container and hides the
|
|
6946
|
+
* overflowing ones. The boundary is the container's own content edge, which
|
|
6947
|
+
* keeps the measurement correct even when the host lives inside a flex layout
|
|
6948
|
+
* (we never depend on a sibling's position). The stop element is used only to
|
|
6949
|
+
* reserve space for the "more" trigger when not all items fit.
|
|
6950
|
+
*
|
|
6951
|
+
* The directive applies `display: none` on the items that do not fit.
|
|
6947
6952
|
*
|
|
6948
6953
|
* You can specify a target element to observe for resize events, otherwise the
|
|
6949
6954
|
* directive will observe the element itself.
|
|
@@ -6982,10 +6987,26 @@ class OverflowManagerDirective {
|
|
|
6982
6987
|
target = input(...(ngDevMode ? [undefined, { debugName: "target" }] : []));
|
|
6983
6988
|
margin = input(4, ...(ngDevMode ? [{ debugName: "margin" }] : []));
|
|
6984
6989
|
direction = input("horizontal", ...(ngDevMode ? [{ debugName: "direction" }] : []));
|
|
6990
|
+
/**
|
|
6991
|
+
* Always reserve the stop element's size, even when every item fits inside
|
|
6992
|
+
* the container. Use it when the stop trigger is permanently visible (e.g.
|
|
6993
|
+
* the "more" button also gives access to items that are never rendered in
|
|
6994
|
+
* the container), so the last item never overlaps it.
|
|
6995
|
+
*/
|
|
6996
|
+
reserveStop = input(false, ...(ngDevMode ? [{ debugName: "reserveStop", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
6985
6997
|
count = output();
|
|
6986
6998
|
el = inject(ElementRef).nativeElement;
|
|
6987
6999
|
destroyRef = inject(DestroyRef);
|
|
6988
7000
|
resizeObserver = new ResizeObserver(() => this.countItems());
|
|
7001
|
+
// Recompute when an individual item's size changes (label update, async
|
|
7002
|
+
// translation, font load…), not only when the container resizes. Items we
|
|
7003
|
+
// hid with `display: none` report a 0×0 size; ignore those notifications so
|
|
7004
|
+
// our own show/hide toggling doesn't trigger redundant recounts.
|
|
7005
|
+
itemsResizeObserver = new ResizeObserver((entries) => {
|
|
7006
|
+
const visibleItemResized = entries.some((entry) => entry.target.style.display !== "none");
|
|
7007
|
+
if (visibleItemResized)
|
|
7008
|
+
this.countItems();
|
|
7009
|
+
});
|
|
6989
7010
|
countSub;
|
|
6990
7011
|
_lastCount;
|
|
6991
7012
|
constructor() {
|
|
@@ -6997,21 +7018,37 @@ class OverflowManagerDirective {
|
|
|
6997
7018
|
this.countItems();
|
|
6998
7019
|
}
|
|
6999
7020
|
});
|
|
7021
|
+
// (re)observe every item whenever the projected list changes, so that
|
|
7022
|
+
// added/removed items and per-item size changes both trigger a recount
|
|
7023
|
+
effect(() => {
|
|
7024
|
+
const items = this.items();
|
|
7025
|
+
this.itemsResizeObserver.disconnect();
|
|
7026
|
+
items.forEach((item) => this.itemsResizeObserver.observe(item.nativeElement));
|
|
7027
|
+
});
|
|
7000
7028
|
// listens to the count output and toggles the visibility of the items
|
|
7001
7029
|
this.countSub = this.count.subscribe((count) => this.toggleToCount(count));
|
|
7002
7030
|
this.destroyRef.onDestroy(() => {
|
|
7003
7031
|
this.resizeObserver.disconnect();
|
|
7032
|
+
this.itemsResizeObserver.disconnect();
|
|
7004
7033
|
this.countSub?.unsubscribe();
|
|
7005
7034
|
this.countSub = undefined;
|
|
7006
7035
|
});
|
|
7007
7036
|
}
|
|
7008
7037
|
/**
|
|
7009
|
-
* Counts the number of items that can fit
|
|
7038
|
+
* Counts the number of items that can fit inside the container.
|
|
7039
|
+
*
|
|
7040
|
+
* The boundary is the container's own content edge (not the position of the
|
|
7041
|
+
* stop element). This is what makes the measurement robust when the host is
|
|
7042
|
+
* placed inside a flex layout: we never rely on a sibling staying where we
|
|
7043
|
+
* expect it. The stop element is only used for its *size*, to reserve space
|
|
7044
|
+
* for the "more" trigger when not all items fit.
|
|
7045
|
+
*
|
|
7010
7046
|
* Emits the count if it has changed.
|
|
7011
7047
|
*/
|
|
7012
7048
|
countItems() {
|
|
7013
7049
|
if (!this.items() || this.items().length === 0 || !this.stop())
|
|
7014
7050
|
return;
|
|
7051
|
+
const horizontal = this.direction() === "horizontal";
|
|
7015
7052
|
// Reset all items to their natural size before measuring so that previously
|
|
7016
7053
|
// hidden items (display: none) don't corrupt the layout and position of
|
|
7017
7054
|
// their siblings.
|
|
@@ -7019,19 +7056,33 @@ class OverflowManagerDirective {
|
|
|
7019
7056
|
item.nativeElement.style.display = "";
|
|
7020
7057
|
});
|
|
7021
7058
|
// getBoundingClientRect() forces a synchronous reflow, so positions are
|
|
7022
|
-
// accurate after the reset above.
|
|
7059
|
+
// accurate after the reset above. Using rects (not offsetWidth) means the
|
|
7060
|
+
// inter-item gap is accounted for automatically.
|
|
7061
|
+
const containerRect = this.el.getBoundingClientRect();
|
|
7023
7062
|
const stopRect = this.stop().nativeElement.getBoundingClientRect();
|
|
7024
7063
|
const itemsRects = this.items().map((item) => item.nativeElement.getBoundingClientRect());
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7064
|
+
// The container's content edge, with a small slack margin.
|
|
7065
|
+
const containerEnd = (horizontal ? containerRect.right : containerRect.bottom) - this.margin();
|
|
7066
|
+
const itemEnd = (rect) => (horizontal ? rect.right : rect.bottom);
|
|
7067
|
+
let count;
|
|
7068
|
+
// If every item fits within the container, no "more" trigger is needed and
|
|
7069
|
+
// we don't reserve any space for it — unless the trigger is permanently
|
|
7070
|
+
// visible (reserveStop), in which case its space is always reserved.
|
|
7071
|
+
if (!this.reserveStop() && itemEnd(itemsRects[itemsRects.length - 1]) <= containerEnd) {
|
|
7072
|
+
count = itemsRects.length;
|
|
7073
|
+
}
|
|
7074
|
+
else {
|
|
7075
|
+
// Otherwise reserve the stop element's own size so the last visible item
|
|
7076
|
+
// never overlaps the "more" trigger.
|
|
7077
|
+
const reserve = horizontal ? stopRect.width : stopRect.height;
|
|
7078
|
+
const limit = containerEnd - reserve;
|
|
7079
|
+
count = 0;
|
|
7080
|
+
for (const rect of itemsRects) {
|
|
7081
|
+
if (itemEnd(rect) <= limit)
|
|
7082
|
+
count++;
|
|
7083
|
+
else
|
|
7084
|
+
break;
|
|
7085
|
+
}
|
|
7035
7086
|
}
|
|
7036
7087
|
if (this._lastCount !== count) {
|
|
7037
7088
|
this._lastCount = count;
|
|
@@ -7054,7 +7105,7 @@ class OverflowManagerDirective {
|
|
|
7054
7105
|
});
|
|
7055
7106
|
}
|
|
7056
7107
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: OverflowManagerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
7057
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.18", type: OverflowManagerDirective, isStandalone: true, selector: "[overflowManager]", inputs: { target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, margin: { classPropertyName: "margin", publicName: "margin", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { count: "count" }, queries: [{ propertyName: "items", predicate: OverflowItemDirective, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "stop", first: true, predicate: OverflowStopDirective, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0 });
|
|
7108
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.18", type: OverflowManagerDirective, isStandalone: true, selector: "[overflowManager]", inputs: { target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, margin: { classPropertyName: "margin", publicName: "margin", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, reserveStop: { classPropertyName: "reserveStop", publicName: "reserveStop", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { count: "count" }, queries: [{ propertyName: "items", predicate: OverflowItemDirective, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "stop", first: true, predicate: OverflowStopDirective, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0 });
|
|
7058
7109
|
}
|
|
7059
7110
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: OverflowManagerDirective, decorators: [{
|
|
7060
7111
|
type: Directive,
|
|
@@ -7062,7 +7113,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
7062
7113
|
selector: "[overflowManager]",
|
|
7063
7114
|
standalone: true
|
|
7064
7115
|
}]
|
|
7065
|
-
}], ctorParameters: () => [], propDecorators: { items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => OverflowItemDirective), { ...{ descendants: true, read: ElementRef }, isSignal: true }] }], stop: [{ type: i0.ContentChild, args: [i0.forwardRef(() => OverflowStopDirective), { ...{ descendants: true, read: ElementRef }, isSignal: true }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], margin: [{ type: i0.Input, args: [{ isSignal: true, alias: "margin", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], count: [{ type: i0.Output, args: ["count"] }] } });
|
|
7116
|
+
}], ctorParameters: () => [], propDecorators: { items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => OverflowItemDirective), { ...{ descendants: true, read: ElementRef }, isSignal: true }] }], stop: [{ type: i0.ContentChild, args: [i0.forwardRef(() => OverflowStopDirective), { ...{ descendants: true, read: ElementRef }, isSignal: true }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], margin: [{ type: i0.Input, args: [{ isSignal: true, alias: "margin", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], reserveStop: [{ type: i0.Input, args: [{ isSignal: true, alias: "reserveStop", required: false }] }], count: [{ type: i0.Output, args: ["count"] }] } });
|
|
7066
7117
|
|
|
7067
7118
|
/**
|
|
7068
7119
|
* Directive that selects an article on click.
|
|
@@ -7299,7 +7350,7 @@ class NavbarTabsComponent {
|
|
|
7299
7350
|
</Menu>
|
|
7300
7351
|
</div>
|
|
7301
7352
|
}
|
|
7302
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size"] }, { kind: "component", type: MenuComponent, selector: "menu, Menu", inputs: ["disabled"] }, { kind: "directive", type: MenuItemComponent, selector: "menu-item, menuitem, MenuItem", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: MenuContentComponent, selector: "MenuContent, menucontent, menu-content", inputs: ["class", "position"] }, { kind: "directive", type: TabsComponent, selector: "tabs, Tabs", inputs: ["class"] }, { kind: "directive", type: TabsListComponent, selector: "tabs-list, TabsList", inputs: ["class", "variant", "size"] }, { kind: "directive", type: TabComponent, selector: "tab, Tab", inputs: ["class", "variant", "size", "noTruncate", "value", "active"], outputs: ["clicked"] }, { kind: "directive", type: OverflowManagerDirective, selector: "[overflowManager]", inputs: ["target", "margin", "direction"], outputs: ["count"] }, { kind: "directive", type: OverflowItemDirective, selector: "[overflowItem]" }, { kind: "directive", type: OverflowStopDirective, selector: "[overflowStop]" }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "size"] }, { kind: "component", type: EllipsisIcon, selector: "ellipsis-icon, EllipsisIcon, ellipsisicon", inputs: ["class", "orientation"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
7353
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size"] }, { kind: "component", type: MenuComponent, selector: "menu, Menu", inputs: ["disabled"] }, { kind: "directive", type: MenuItemComponent, selector: "menu-item, menuitem, MenuItem", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: MenuContentComponent, selector: "MenuContent, menucontent, menu-content", inputs: ["class", "position"] }, { kind: "directive", type: TabsComponent, selector: "tabs, Tabs", inputs: ["class"] }, { kind: "directive", type: TabsListComponent, selector: "tabs-list, TabsList", inputs: ["class", "variant", "size"] }, { kind: "directive", type: TabComponent, selector: "tab, Tab", inputs: ["class", "variant", "size", "noTruncate", "value", "active"], outputs: ["clicked"] }, { kind: "directive", type: OverflowManagerDirective, selector: "[overflowManager]", inputs: ["target", "margin", "direction", "reserveStop"], outputs: ["count"] }, { kind: "directive", type: OverflowItemDirective, selector: "[overflowItem]" }, { kind: "directive", type: OverflowStopDirective, selector: "[overflowStop]" }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "size"] }, { kind: "component", type: EllipsisIcon, selector: "ellipsis-icon, EllipsisIcon, ellipsisicon", inputs: ["class", "orientation"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
7303
7354
|
}
|
|
7304
7355
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: NavbarTabsComponent, decorators: [{
|
|
7305
7356
|
type: Component,
|
|
@@ -10690,6 +10741,15 @@ class SignInComponent {
|
|
|
10690
10741
|
destroyRef;
|
|
10691
10742
|
cn = cn;
|
|
10692
10743
|
config = globalConfig;
|
|
10744
|
+
/**
|
|
10745
|
+
* True when authentication is handled outside the credentials form — i.e. by the
|
|
10746
|
+
* browser/proxy (`useSSO`) or by an auto-configured OAuth/SAML provider. In those
|
|
10747
|
+
* modes this screen shows a loader instead of a login form and initiates the
|
|
10748
|
+
* handshake automatically by calling `handleLogin()`.
|
|
10749
|
+
*/
|
|
10750
|
+
externalAuth = !!(globalConfig.useSSO ||
|
|
10751
|
+
globalConfig.autoOAuthProvider ||
|
|
10752
|
+
globalConfig.autoSAMLProvider);
|
|
10693
10753
|
class = input(...(ngDevMode ? [undefined, { debugName: "class" }] : []));
|
|
10694
10754
|
forgotPassword = output();
|
|
10695
10755
|
username = model("", ...(ngDevMode ? [{ debugName: "username" }] : []));
|
|
@@ -10711,6 +10771,30 @@ class SignInComponent {
|
|
|
10711
10771
|
expiresSoonNotified = signal(false, ...(ngDevMode ? [{ debugName: "expiresSoonNotified" }] : []));
|
|
10712
10772
|
constructor(destroyRef) {
|
|
10713
10773
|
this.destroyRef = destroyRef;
|
|
10774
|
+
// If the user is already authenticated when landing here (e.g. page refresh on
|
|
10775
|
+
// /login, or an external handshake completed before this screen was created),
|
|
10776
|
+
// don't sit on the loader: go straight to the returnUrl.
|
|
10777
|
+
if (this.authenticated()) {
|
|
10778
|
+
const url = this.route.snapshot.queryParams["returnUrl"] || "/";
|
|
10779
|
+
this.router.navigateByUrl(url);
|
|
10780
|
+
}
|
|
10781
|
+
// When authentication is delegated to the browser/proxy (SSO) or an OAuth/SAML
|
|
10782
|
+
// provider, no credentials form is shown: this screen shows a loader and initiates
|
|
10783
|
+
// the handshake automatically by calling `handleLogin()`. If the handshake never
|
|
10784
|
+
// completes, fall back to /error after 5s; the fallback is cancelled as soon as
|
|
10785
|
+
// the login succeeds (the `authenticated` event then drives navigation).
|
|
10786
|
+
if (this.externalAuth && !this.authenticated()) {
|
|
10787
|
+
const timeout = setTimeout(() => {
|
|
10788
|
+
this.router.navigate(["/error"], {
|
|
10789
|
+
queryParams: { returnUrl: this.route.snapshot.queryParams["returnUrl"] }
|
|
10790
|
+
});
|
|
10791
|
+
}, 5000);
|
|
10792
|
+
destroyRef.onDestroy(() => clearTimeout(timeout));
|
|
10793
|
+
this.handleLogin().then(result => {
|
|
10794
|
+
if (result)
|
|
10795
|
+
clearTimeout(timeout);
|
|
10796
|
+
});
|
|
10797
|
+
}
|
|
10714
10798
|
effect(() => {
|
|
10715
10799
|
const principal = getState(this.principalStore);
|
|
10716
10800
|
if (this.authenticated() && principal && !this.expiresSoonNotified()) {
|
|
@@ -10756,14 +10840,16 @@ class SignInComponent {
|
|
|
10756
10840
|
this.router.navigate(["/login"]);
|
|
10757
10841
|
}
|
|
10758
10842
|
async handleLogin() {
|
|
10759
|
-
login().then((result) => {
|
|
10843
|
+
return login().then((result) => {
|
|
10760
10844
|
if (result) {
|
|
10761
10845
|
this.auditService.notifyLogin();
|
|
10762
10846
|
}
|
|
10847
|
+
return result;
|
|
10763
10848
|
}).catch(error => {
|
|
10764
10849
|
warn("An error occurred while logging in", error);
|
|
10765
10850
|
this.auditService.notify({ type: 'Login_Denied' });
|
|
10766
10851
|
this.router.navigate(["error"]);
|
|
10852
|
+
return false;
|
|
10767
10853
|
});
|
|
10768
10854
|
}
|
|
10769
10855
|
async handleLoginWithCredentials() {
|
|
@@ -10806,7 +10892,7 @@ class SignInComponent {
|
|
|
10806
10892
|
}
|
|
10807
10893
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: SignInComponent, deps: [{ token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
10808
10894
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: SignInComponent, isStandalone: true, selector: "signIn, signin, sign-in", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { forgotPassword: "forgotPassword", username: "usernameChange", password: "passwordChange" }, host: { properties: { "class": "cn('grid h-dvh w-full place-content-center', class())" } }, providers: [provideTranslocoScope("login")], ngImport: i0, template: `
|
|
10809
|
-
@if (!authenticated()) {
|
|
10895
|
+
@if (!authenticated() && !externalAuth) {
|
|
10810
10896
|
<Card
|
|
10811
10897
|
hover="no"
|
|
10812
10898
|
cdkTrapFocus
|
|
@@ -10817,60 +10903,54 @@ class SignInComponent {
|
|
|
10817
10903
|
</CardHeader>
|
|
10818
10904
|
|
|
10819
10905
|
<CardContent class="grid gap-4">
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
(click)="handleLoginWithCredentials()">
|
|
10859
|
-
{{ "login.connect" | transloco }}
|
|
10860
|
-
</button>
|
|
10861
|
-
}
|
|
10862
|
-
@else {
|
|
10863
|
-
<!-- authentication using OAuth or SAML provider -->
|
|
10864
|
-
<button (click)="handleLogin()">
|
|
10865
|
-
{{ "login.SignInWith" | transloco : { provider: config.autoOAuthProvider ? "OAuth" : "SAML" } }}
|
|
10866
|
-
</button>
|
|
10867
|
-
}
|
|
10906
|
+
<!-- authentication using credentials -->
|
|
10907
|
+
<div class="grid gap-2">
|
|
10908
|
+
<label class="text-sm font-medium" for="username">{{
|
|
10909
|
+
"login.username" | transloco
|
|
10910
|
+
}}</label>
|
|
10911
|
+
<input
|
|
10912
|
+
id="username"
|
|
10913
|
+
type="text"
|
|
10914
|
+
required
|
|
10915
|
+
[(ngModel)]="username"
|
|
10916
|
+
(keydown.enter)="handleLoginWithCredentials()" />
|
|
10917
|
+
</div>
|
|
10918
|
+
|
|
10919
|
+
<div class="grid gap-2">
|
|
10920
|
+
<label class="text-sm font-medium" for="password">{{
|
|
10921
|
+
"login.password" | transloco
|
|
10922
|
+
}}</label>
|
|
10923
|
+
<input
|
|
10924
|
+
id="password"
|
|
10925
|
+
type="password"
|
|
10926
|
+
required
|
|
10927
|
+
[(ngModel)]="password"
|
|
10928
|
+
(keydown.enter)="handleLoginWithCredentials()" />
|
|
10929
|
+
</div>
|
|
10930
|
+
|
|
10931
|
+
<span
|
|
10932
|
+
class="text-muted-foreground cursor-pointer justify-self-start text-xs hover:underline"
|
|
10933
|
+
role="button"
|
|
10934
|
+
tabindex="0"
|
|
10935
|
+
(click)="forgotPassword.emit()"
|
|
10936
|
+
(keydown.enter)="forgotPassword.emit()">
|
|
10937
|
+
{{ "login.forgotPassword" | transloco }}
|
|
10938
|
+
</span>
|
|
10939
|
+
<button variant="primary"
|
|
10940
|
+
[disabled]="!isValid()"
|
|
10941
|
+
(click)="handleLoginWithCredentials()">
|
|
10942
|
+
{{ "login.connect" | transloco }}
|
|
10943
|
+
</button>
|
|
10868
10944
|
</CardContent>
|
|
10869
10945
|
</Card>
|
|
10870
10946
|
} @else {
|
|
10871
|
-
<
|
|
10947
|
+
<div class="flex h-dvh w-full items-center justify-center">
|
|
10948
|
+
<div class="flex flex-col items-center space-y-4">
|
|
10949
|
+
<span class="loader"></span>
|
|
10950
|
+
</div>
|
|
10951
|
+
</div>
|
|
10872
10952
|
}
|
|
10873
|
-
`, isInline: true, styles: ["input{background-color:var(--background)}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: InputComponent, selector: "input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"], input[type=\"tel\"], input[type=\"url\"], input[type=\"time\"], input[type=\"file\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size"] }, { kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }, { kind: "
|
|
10953
|
+
`, isInline: true, styles: ["input{background-color:var(--background)}.loader{--w: 96px;--h: 96px;transform:rotate(45deg);perspective:1000px;border-radius:50%;width:var(--w);height:var(--h);color:#0040bf}.loader:before,.loader:after{content:\"\";display:block;position:absolute;top:0;left:0;width:inherit;height:inherit;border-radius:50%;transform:rotateX(70deg);animation:1s spin linear infinite}.loader:after{color:#ff854a;transform:rotateY(70deg);animation-delay:.4s}@keyframes spin{0%,to{box-shadow:.4em 0 0 0 currentcolor}12%{box-shadow:.4em .4em 0 0 currentcolor}25%{box-shadow:0 .4em 0 0 currentcolor}37%{box-shadow:-.4em .4em 0 0 currentcolor}50%{box-shadow:-.4em 0 0 0 currentcolor}62%{box-shadow:-.4em -.4em 0 0 currentcolor}75%{box-shadow:0 -.4em 0 0 currentcolor}87%{box-shadow:.4em -.4em 0 0 currentcolor}}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: InputComponent, selector: "input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"], input[type=\"tel\"], input[type=\"url\"], input[type=\"time\"], input[type=\"file\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size"] }, { kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
10874
10954
|
}
|
|
10875
10955
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: SignInComponent, decorators: [{
|
|
10876
10956
|
type: Component,
|
|
@@ -10883,10 +10963,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
10883
10963
|
ButtonComponent,
|
|
10884
10964
|
CardComponent,
|
|
10885
10965
|
CardHeaderComponent,
|
|
10886
|
-
CardContentComponent
|
|
10887
|
-
LoadingComponent
|
|
10966
|
+
CardContentComponent
|
|
10888
10967
|
], providers: [provideTranslocoScope("login")], template: `
|
|
10889
|
-
@if (!authenticated()) {
|
|
10968
|
+
@if (!authenticated() && !externalAuth) {
|
|
10890
10969
|
<Card
|
|
10891
10970
|
hover="no"
|
|
10892
10971
|
cdkTrapFocus
|
|
@@ -10897,62 +10976,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
10897
10976
|
</CardHeader>
|
|
10898
10977
|
|
|
10899
10978
|
<CardContent class="grid gap-4">
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
(click)="handleLoginWithCredentials()">
|
|
10939
|
-
{{ "login.connect" | transloco }}
|
|
10940
|
-
</button>
|
|
10941
|
-
}
|
|
10942
|
-
@else {
|
|
10943
|
-
<!-- authentication using OAuth or SAML provider -->
|
|
10944
|
-
<button (click)="handleLogin()">
|
|
10945
|
-
{{ "login.SignInWith" | transloco : { provider: config.autoOAuthProvider ? "OAuth" : "SAML" } }}
|
|
10946
|
-
</button>
|
|
10947
|
-
}
|
|
10979
|
+
<!-- authentication using credentials -->
|
|
10980
|
+
<div class="grid gap-2">
|
|
10981
|
+
<label class="text-sm font-medium" for="username">{{
|
|
10982
|
+
"login.username" | transloco
|
|
10983
|
+
}}</label>
|
|
10984
|
+
<input
|
|
10985
|
+
id="username"
|
|
10986
|
+
type="text"
|
|
10987
|
+
required
|
|
10988
|
+
[(ngModel)]="username"
|
|
10989
|
+
(keydown.enter)="handleLoginWithCredentials()" />
|
|
10990
|
+
</div>
|
|
10991
|
+
|
|
10992
|
+
<div class="grid gap-2">
|
|
10993
|
+
<label class="text-sm font-medium" for="password">{{
|
|
10994
|
+
"login.password" | transloco
|
|
10995
|
+
}}</label>
|
|
10996
|
+
<input
|
|
10997
|
+
id="password"
|
|
10998
|
+
type="password"
|
|
10999
|
+
required
|
|
11000
|
+
[(ngModel)]="password"
|
|
11001
|
+
(keydown.enter)="handleLoginWithCredentials()" />
|
|
11002
|
+
</div>
|
|
11003
|
+
|
|
11004
|
+
<span
|
|
11005
|
+
class="text-muted-foreground cursor-pointer justify-self-start text-xs hover:underline"
|
|
11006
|
+
role="button"
|
|
11007
|
+
tabindex="0"
|
|
11008
|
+
(click)="forgotPassword.emit()"
|
|
11009
|
+
(keydown.enter)="forgotPassword.emit()">
|
|
11010
|
+
{{ "login.forgotPassword" | transloco }}
|
|
11011
|
+
</span>
|
|
11012
|
+
<button variant="primary"
|
|
11013
|
+
[disabled]="!isValid()"
|
|
11014
|
+
(click)="handleLoginWithCredentials()">
|
|
11015
|
+
{{ "login.connect" | transloco }}
|
|
11016
|
+
</button>
|
|
10948
11017
|
</CardContent>
|
|
10949
11018
|
</Card>
|
|
10950
11019
|
} @else {
|
|
10951
|
-
<
|
|
11020
|
+
<div class="flex h-dvh w-full items-center justify-center">
|
|
11021
|
+
<div class="flex flex-col items-center space-y-4">
|
|
11022
|
+
<span class="loader"></span>
|
|
11023
|
+
</div>
|
|
11024
|
+
</div>
|
|
10952
11025
|
}
|
|
10953
11026
|
`, host: {
|
|
10954
11027
|
"[class]": "cn('grid h-dvh w-full place-content-center', class())"
|
|
10955
|
-
}, styles: ["input{background-color:var(--background)}\n"] }]
|
|
11028
|
+
}, styles: ["input{background-color:var(--background)}.loader{--w: 96px;--h: 96px;transform:rotate(45deg);perspective:1000px;border-radius:50%;width:var(--w);height:var(--h);color:#0040bf}.loader:before,.loader:after{content:\"\";display:block;position:absolute;top:0;left:0;width:inherit;height:inherit;border-radius:50%;transform:rotateX(70deg);animation:1s spin linear infinite}.loader:after{color:#ff854a;transform:rotateY(70deg);animation-delay:.4s}@keyframes spin{0%,to{box-shadow:.4em 0 0 0 currentcolor}12%{box-shadow:.4em .4em 0 0 currentcolor}25%{box-shadow:0 .4em 0 0 currentcolor}37%{box-shadow:-.4em .4em 0 0 currentcolor}50%{box-shadow:-.4em 0 0 0 currentcolor}62%{box-shadow:-.4em -.4em 0 0 currentcolor}75%{box-shadow:0 -.4em 0 0 currentcolor}87%{box-shadow:.4em -.4em 0 0 currentcolor}}\n"] }]
|
|
10956
11029
|
}], ctorParameters: () => [{ type: i0.DestroyRef }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], forgotPassword: [{ type: i0.Output, args: ["forgotPassword"] }], username: [{ type: i0.Input, args: [{ isSignal: true, alias: "username", required: false }] }, { type: i0.Output, args: ["usernameChange"] }], password: [{ type: i0.Input, args: [{ isSignal: true, alias: "password", required: false }] }, { type: i0.Output, args: ["passwordChange"] }] } });
|
|
10957
11030
|
|
|
10958
11031
|
class AuthPageComponent {
|
|
@@ -14610,50 +14683,76 @@ class FiltersBarComponent {
|
|
|
14610
14683
|
return this.aggregationsStore.aggregations().length > 0;
|
|
14611
14684
|
return false;
|
|
14612
14685
|
}, ...(ngDevMode ? [{ debugName: "hasAggregations" }] : []));
|
|
14686
|
+
/**
|
|
14687
|
+
* The full list of authorized filters, NOT capped by `filtersCount`.
|
|
14688
|
+
*
|
|
14689
|
+
* This computed signal performs the following operations:
|
|
14690
|
+
* 1. Retrieves aggregations from either the component's aggregations input or the app store
|
|
14691
|
+
* 2. Filters aggregations based on the route's filter criteria configuration
|
|
14692
|
+
* 3. Excludes filters specified in the `excludeFilters` list
|
|
14693
|
+
* 4. If `includeFilters` is not empty, only includes filters present in that list
|
|
14694
|
+
* 5. Maps the filtered aggregations to objects containing only `name` and `column` properties
|
|
14695
|
+
*/
|
|
14696
|
+
allAuthorizedFilters = computed(() => {
|
|
14697
|
+
return this.aggregationsService
|
|
14698
|
+
.getAuthorizedFilters(this.aggregations(), this.includeFilters(), this.excludeFilters(), this.homepage())
|
|
14699
|
+
.map((f) => ({ name: f.name, column: f.column }));
|
|
14700
|
+
}, ...(ngDevMode ? [{ debugName: "allAuthorizedFilters" }] : []));
|
|
14613
14701
|
/**
|
|
14614
14702
|
* Computes the list of additional filters that can be displayed in the "more filters" popover.
|
|
14615
14703
|
*
|
|
14616
|
-
*
|
|
14617
|
-
*
|
|
14618
|
-
*
|
|
14704
|
+
* Derived from the FULL authorized list (not the one capped by `filtersCount`), so the
|
|
14705
|
+
* filters beyond `filtersCount` — which are never rendered in the bar — are still counted.
|
|
14706
|
+
* Otherwise, when every rendered filter fits in the container, this list would be empty and
|
|
14707
|
+
* the "more" button would be hidden even though more filters exist beyond the cap.
|
|
14619
14708
|
*
|
|
14620
|
-
* This property manages the visibility
|
|
14709
|
+
* This property manages the visibility of the "more filters" button in the UI.
|
|
14621
14710
|
*
|
|
14622
14711
|
* @returns An array of Aggregation objects representing the additional filters available.
|
|
14623
14712
|
*/
|
|
14624
14713
|
hasMoreFilters = computed(() => {
|
|
14625
|
-
const moreFiltersAggregations = this.
|
|
14626
|
-
.filter((f) => !this.excludeFilters().includes(f.name)) // filter out the excluded filters
|
|
14627
|
-
.filter((f) => !this.includeFilters().length || this.includeFilters().includes(f.name)) // exclude filters not included in includeFilters if not empty
|
|
14628
|
-
.map((f) => ({ column: f.column, name: f.name }))
|
|
14714
|
+
const moreFiltersAggregations = this.allAuthorizedFilters()
|
|
14629
14715
|
.toSpliced(0, this.visibleFiltersCount())
|
|
14630
14716
|
.map((f) => this.aggregationsStore.getAggregation(f.column, "column"));
|
|
14631
14717
|
return moreFiltersAggregations;
|
|
14632
14718
|
}, ...(ngDevMode ? [{ debugName: "hasMoreFilters" }] : []));
|
|
14633
14719
|
/**
|
|
14634
|
-
*
|
|
14635
|
-
*
|
|
14636
|
-
* This computed signal performs the following operations:
|
|
14637
|
-
* 1. Retrieves aggregations from either the component's aggregations input or the app store
|
|
14638
|
-
* 2. Filters aggregations based on the route's filter criteria configuration
|
|
14639
|
-
* 3. Excludes filters specified in the `excludeFilters` list
|
|
14640
|
-
* 4. If `includeFilters` is not empty, only includes filters present in that list
|
|
14641
|
-
* 5. Maps the filtered aggregations to objects containing only `name` and `column` properties
|
|
14642
|
-
* 6. Limits the result to the number specified by `filtersCount`
|
|
14720
|
+
* The authorized filters rendered as buttons in the bar, limited to the number
|
|
14721
|
+
* specified by `filtersCount`.
|
|
14643
14722
|
*
|
|
14644
14723
|
* @returns An array of authorized filter objects, each containing `name` and `column` properties
|
|
14645
14724
|
*/
|
|
14646
14725
|
authorizedFilters = computed(() => {
|
|
14647
|
-
|
|
14648
|
-
.getAuthorizedFilters(this.aggregations(), this.includeFilters(), this.excludeFilters(), this.homepage())
|
|
14649
|
-
.map((f) => ({ name: f.name, column: f.column }))
|
|
14650
|
-
.toSpliced(this.filtersCount());
|
|
14651
|
-
return authorizedFilters;
|
|
14726
|
+
return this.allAuthorizedFilters().toSpliced(this.filtersCount());
|
|
14652
14727
|
}, ...(ngDevMode ? [{ debugName: "authorizedFilters" }] : []));
|
|
14728
|
+
/**
|
|
14729
|
+
* Whether some authorized filters exist beyond the `filtersCount` cap.
|
|
14730
|
+
*
|
|
14731
|
+
* Those filters are never rendered in the bar and are only reachable through
|
|
14732
|
+
* the "more" button, which is therefore permanently visible: the overflow
|
|
14733
|
+
* manager must always reserve its space so the last filter button never
|
|
14734
|
+
* overlaps it (`reserveStop`).
|
|
14735
|
+
*/
|
|
14736
|
+
hasCappedFilters = computed(() => this.allAuthorizedFilters().length > this.filtersCount(), ...(ngDevMode ? [{ debugName: "hasCappedFilters" }] : []));
|
|
14653
14737
|
constructor() {
|
|
14654
14738
|
this.transloco.events$
|
|
14655
14739
|
.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(100))
|
|
14656
14740
|
.subscribe(() => this.overflowManagerRef()?.countItems());
|
|
14741
|
+
// Recount the overflow whenever the applied filters or basket change (e.g.
|
|
14742
|
+
// a filter modified or removed from the "more filters" popover). A
|
|
14743
|
+
// FilterButton hidden by the overflow manager (display: none) emits no
|
|
14744
|
+
// resize notification when its natural width changes, so it could fit in
|
|
14745
|
+
// the bar again without the manager knowing. afterRenderEffect guarantees
|
|
14746
|
+
// the DOM already reflects the new state when we measure.
|
|
14747
|
+
afterRenderEffect({
|
|
14748
|
+
read: () => {
|
|
14749
|
+
// track filters and basket changes (getState is reactive here)
|
|
14750
|
+
const { filters, basket } = getState(this.queryParamsStore);
|
|
14751
|
+
void filters;
|
|
14752
|
+
void basket;
|
|
14753
|
+
this.overflowManagerRef()?.countItems();
|
|
14754
|
+
}
|
|
14755
|
+
});
|
|
14657
14756
|
}
|
|
14658
14757
|
/**
|
|
14659
14758
|
* Clears all filters (included baskets) by invoking the clearFilters method on the queryParamsStore.
|
|
@@ -14702,8 +14801,8 @@ class FiltersBarComponent {
|
|
|
14702
14801
|
});
|
|
14703
14802
|
}
|
|
14704
14803
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: FiltersBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14705
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: FiltersBarComponent, isStandalone: true, selector: "filters-bar, FiltersBar, filtersbar", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, morePosition: { classPropertyName: "morePosition", publicName: "morePosition", isSignal: true, isRequired: false, transformFunction: null }, aggregations: { classPropertyName: "aggregations", publicName: "aggregations", isSignal: true, isRequired: false, transformFunction: null }, includeFilters: { classPropertyName: "includeFilters", publicName: "includeFilters", isSignal: true, isRequired: false, transformFunction: null }, excludeFilters: { classPropertyName: "excludeFilters", publicName: "excludeFilters", isSignal: true, isRequired: false, transformFunction: null }, filtersCount: { classPropertyName: "filtersCount", publicName: "filtersCount", isSignal: true, isRequired: false, transformFunction: null }, showMoreFiltersButton: { classPropertyName: "showMoreFiltersButton", publicName: "showMoreFiltersButton", isSignal: true, isRequired: false, transformFunction: null }, homepage: { classPropertyName: "homepage", publicName: "homepage", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onClearFilters: "onClearFilters", onClearBasket: "onClearBasket" }, host: { listeners: { "click": "handleClick($event)" }, properties: { "class": "cn('block relative', class())" } }, providers: [provideTranslocoScope("filters")], viewQueries: [{ propertyName: "moreButtonRef", first: true, predicate: MoreButtonComponent, descendants: true, isSignal: true }, { propertyName: "filterButtonRefs", predicate: FilterButtonComponent, descendants: true, isSignal: true }, { propertyName: "overflowManagerRef", first: true, predicate: OverflowManagerDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
14706
|
-
<div overflowManager [direction]="direction()" (count)="adjustFiltersCount($event)" class="flex items-end gap-2 rounded-[inherit] bg-inherit">
|
|
14804
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: FiltersBarComponent, isStandalone: true, selector: "filters-bar, FiltersBar, filtersbar", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, morePosition: { classPropertyName: "morePosition", publicName: "morePosition", isSignal: true, isRequired: false, transformFunction: null }, aggregations: { classPropertyName: "aggregations", publicName: "aggregations", isSignal: true, isRequired: false, transformFunction: null }, includeFilters: { classPropertyName: "includeFilters", publicName: "includeFilters", isSignal: true, isRequired: false, transformFunction: null }, excludeFilters: { classPropertyName: "excludeFilters", publicName: "excludeFilters", isSignal: true, isRequired: false, transformFunction: null }, filtersCount: { classPropertyName: "filtersCount", publicName: "filtersCount", isSignal: true, isRequired: false, transformFunction: null }, showMoreFiltersButton: { classPropertyName: "showMoreFiltersButton", publicName: "showMoreFiltersButton", isSignal: true, isRequired: false, transformFunction: null }, homepage: { classPropertyName: "homepage", publicName: "homepage", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onClearFilters: "onClearFilters", onClearBasket: "onClearBasket" }, host: { listeners: { "click": "handleClick($event)" }, properties: { "class": "cn('block relative min-w-0', class())" } }, providers: [provideTranslocoScope("filters")], viewQueries: [{ propertyName: "moreButtonRef", first: true, predicate: MoreButtonComponent, descendants: true, isSignal: true }, { propertyName: "filterButtonRefs", predicate: FilterButtonComponent, descendants: true, isSignal: true }, { propertyName: "overflowManagerRef", first: true, predicate: OverflowManagerDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
14805
|
+
<div overflowManager [direction]="direction()" [reserveStop]="hasCappedFilters()" (count)="adjustFiltersCount($event)" class="flex items-end gap-2 rounded-[inherit] bg-inherit">
|
|
14707
14806
|
@if (hasFilters()) {
|
|
14708
14807
|
<button
|
|
14709
14808
|
variant="destructive"
|
|
@@ -14755,7 +14854,7 @@ class FiltersBarComponent {
|
|
|
14755
14854
|
}
|
|
14756
14855
|
}
|
|
14757
14856
|
</div>
|
|
14758
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size"] }, { kind: "component", type: MoreButtonComponent, selector: "more-button, MoreButton", inputs: ["count", "position", "includedFilters", "excludedFilters", "aggregations", "homepage"] }, { kind: "component", type: FilterButtonComponent, selector: "filter-button, FilterButton", inputs: ["name", "column", "position", "offset", "expandedLevel"] }, { kind: "directive", type: OverflowManagerDirective, selector: "[overflowManager]", inputs: ["target", "margin", "direction"], outputs: ["count"] }, { kind: "directive", type: OverflowItemDirective, selector: "[overflowItem]" }, { kind: "directive", type: OverflowStopDirective, selector: "[overflowStop]" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
14857
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size"] }, { kind: "component", type: MoreButtonComponent, selector: "more-button, MoreButton", inputs: ["count", "position", "includedFilters", "excludedFilters", "aggregations", "homepage"] }, { kind: "component", type: FilterButtonComponent, selector: "filter-button, FilterButton", inputs: ["name", "column", "position", "offset", "expandedLevel"] }, { kind: "directive", type: OverflowManagerDirective, selector: "[overflowManager]", inputs: ["target", "margin", "direction", "reserveStop"], outputs: ["count"] }, { kind: "directive", type: OverflowItemDirective, selector: "[overflowItem]" }, { kind: "directive", type: OverflowStopDirective, selector: "[overflowStop]" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
14759
14858
|
}
|
|
14760
14859
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: FiltersBarComponent, decorators: [{
|
|
14761
14860
|
type: Component,
|
|
@@ -14773,7 +14872,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
14773
14872
|
],
|
|
14774
14873
|
providers: [provideTranslocoScope("filters")],
|
|
14775
14874
|
template: `
|
|
14776
|
-
<div overflowManager [direction]="direction()" (count)="adjustFiltersCount($event)" class="flex items-end gap-2 rounded-[inherit] bg-inherit">
|
|
14875
|
+
<div overflowManager [direction]="direction()" [reserveStop]="hasCappedFilters()" (count)="adjustFiltersCount($event)" class="flex items-end gap-2 rounded-[inherit] bg-inherit">
|
|
14777
14876
|
@if (hasFilters()) {
|
|
14778
14877
|
<button
|
|
14779
14878
|
variant="destructive"
|
|
@@ -14827,7 +14926,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
14827
14926
|
</div>
|
|
14828
14927
|
`,
|
|
14829
14928
|
host: {
|
|
14830
|
-
"[class]": "cn('block relative', class())",
|
|
14929
|
+
"[class]": "cn('block relative min-w-0', class())",
|
|
14831
14930
|
"(click)": "handleClick($event)"
|
|
14832
14931
|
}
|
|
14833
14932
|
}]
|