@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.
@@ -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 or loading page based on the authentication method
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
- if (useCredentials) {
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 the "loading" route and duplicate navigations.
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
- // TODO: use a "loading" configuration from globalConfig
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 element that should be considered as the limit
6929
- * for the overflow manager.
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 takes items and a stop element to count the number of items
6944
- * that can fit before the stop element.
6945
- * The directive will apply a `visibility: hidden` on the items that are not
6946
- * visible.
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 before the stop element.
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
- let count = 0;
7026
- for (const rect of itemsRects) {
7027
- // if the direction is horizontal, we check the right side of the item
7028
- if (this.direction() === "horizontal" && rect.right + this.margin() <= stopRect.left)
7029
- count++;
7030
- // if the direction is vertical, we check the bottom side of the item
7031
- else if (this.direction() === "vertical" && rect.bottom + this.margin() <= stopRect.top)
7032
- count++;
7033
- else
7034
- break;
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
- @let useCredentials =
10821
- !config.autoOAuthProvider && !config.autoSAMLProvider;
10822
- @if (useCredentials) {
10823
- <!-- authentication using credentials -->
10824
- <div class="grid gap-2">
10825
- <label class="text-sm font-medium" for="username">{{
10826
- "login.username" | transloco
10827
- }}</label>
10828
- <input
10829
- id="username"
10830
- type="text"
10831
- required
10832
- [(ngModel)]="username"
10833
- (keydown.enter)="handleLoginWithCredentials()" />
10834
- </div>
10835
-
10836
- <div class="grid gap-2">
10837
- <label class="text-sm font-medium" for="password">{{
10838
- "login.password" | transloco
10839
- }}</label>
10840
- <input
10841
- id="password"
10842
- type="password"
10843
- required
10844
- [(ngModel)]="password"
10845
- (keydown.enter)="handleLoginWithCredentials()" />
10846
- </div>
10847
-
10848
- <span
10849
- class="text-muted-foreground cursor-pointer justify-self-start text-xs hover:underline"
10850
- role="button"
10851
- tabindex="0"
10852
- (click)="forgotPassword.emit()"
10853
- (keydown.enter)="forgotPassword.emit()">
10854
- {{ "login.forgotPassword" | transloco }}
10855
- </span>
10856
- <button variant="primary"
10857
- [disabled]="!isValid()"
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
- <app-wait />
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: "component", type: LoadingComponent, selector: "app-wait" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
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
- @let useCredentials =
10901
- !config.autoOAuthProvider && !config.autoSAMLProvider;
10902
- @if (useCredentials) {
10903
- <!-- authentication using credentials -->
10904
- <div class="grid gap-2">
10905
- <label class="text-sm font-medium" for="username">{{
10906
- "login.username" | transloco
10907
- }}</label>
10908
- <input
10909
- id="username"
10910
- type="text"
10911
- required
10912
- [(ngModel)]="username"
10913
- (keydown.enter)="handleLoginWithCredentials()" />
10914
- </div>
10915
-
10916
- <div class="grid gap-2">
10917
- <label class="text-sm font-medium" for="password">{{
10918
- "login.password" | transloco
10919
- }}</label>
10920
- <input
10921
- id="password"
10922
- type="password"
10923
- required
10924
- [(ngModel)]="password"
10925
- (keydown.enter)="handleLoginWithCredentials()" />
10926
- </div>
10927
-
10928
- <span
10929
- class="text-muted-foreground cursor-pointer justify-self-start text-xs hover:underline"
10930
- role="button"
10931
- tabindex="0"
10932
- (click)="forgotPassword.emit()"
10933
- (keydown.enter)="forgotPassword.emit()">
10934
- {{ "login.forgotPassword" | transloco }}
10935
- </span>
10936
- <button variant="primary"
10937
- [disabled]="!isValid()"
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
- <app-wait />
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
- * This computed property filters the authorized filters from the AppStore, excluding those
14617
- * specified in the `excludeFilters` input. It then maps these filters to their corresponding
14618
- * aggregations from the AggregationsStore, limited to the number defined by `moreFilterCount`.
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 and content of the "more filters" popover in the UI.
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.authorizedFilters()
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
- * Computed property that returns a filtered and processed list of authorized filters.
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
- const authorizedFilters = this.aggregationsService
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
  }]