@quadrel-enterprise-ui/framework 20.11.2 → 20.13.0

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,15 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, ElementRef, Directive, InjectionToken, HostBinding, Input, ViewEncapsulation, Component, Injectable, Injector, HostListener, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, NgModule, EventEmitter, Output, Renderer2, Pipe, ViewContainerRef, NO_ERRORS_SCHEMA, SecurityContext, NgZone, ViewChildren, forwardRef, DestroyRef, ContentChildren, ContentChild, isDevMode, QueryList, CUSTOM_ELEMENTS_SCHEMA, provideAppInitializer, TemplateRef } from '@angular/core';
2
+ import { inject, ElementRef, Directive, InjectionToken, HostBinding, Input, ViewEncapsulation, Component, Injectable, Injector, HostListener, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, NgModule, EventEmitter, Output, Renderer2, Pipe, ViewContainerRef, NO_ERRORS_SCHEMA, DestroyRef, SecurityContext, NgZone, ViewChildren, forwardRef, ContentChildren, ContentChild, isDevMode, QueryList, CUSTOM_ELEMENTS_SCHEMA, provideAppInitializer, TemplateRef } from '@angular/core';
3
3
  import { Dialog, DialogRef, DialogModule } from '@angular/cdk/dialog';
4
4
  import * as i1 from '@angular/common';
5
5
  import { CommonModule, NgFor, NgIf, NgClass, NgTemplateOutlet, AsyncPipe } from '@angular/common';
6
- import { BehaviorSubject, pairwise, from, switchMap, map as map$1, Subject, throwError, of, ReplaySubject, merge, fromEvent, isObservable, NEVER, Observable, EMPTY, shareReplay, Subscription, distinctUntilChanged as distinctUntilChanged$1, debounce, timer, startWith as startWith$1, debounceTime as debounceTime$1, takeUntil as takeUntil$1, firstValueFrom, combineLatest, concat, take as take$1, delay, tap as tap$1, first, scan, queueScheduler, filter as filter$1, concatMap as concatMap$1, exhaustMap, finalize, combineLatestWith, forkJoin, delayWhen, withLatestFrom, async } from 'rxjs';
7
- import { map, takeUntil, take, filter, catchError, debounceTime, startWith, distinctUntilChanged, concatMap, tap, skip, auditTime, observeOn, switchMap as switchMap$1, pairwise as pairwise$1, mergeMap, delay as delay$1 } from 'rxjs/operators';
6
+ import { BehaviorSubject, pairwise, from, switchMap, map as map$1, Subject, throwError, of, ReplaySubject, merge, fromEvent, isObservable, NEVER, Observable, EMPTY, shareReplay, Subscription, distinctUntilChanged as distinctUntilChanged$1, debounce, timer, startWith as startWith$1, debounceTime as debounceTime$1, takeUntil as takeUntil$1, firstValueFrom, combineLatest, concat, take as take$1, delay, tap as tap$1, first, scan, queueScheduler, filter as filter$1, concatMap as concatMap$1, exhaustMap, finalize, forkJoin, delayWhen, combineLatestWith, withLatestFrom as withLatestFrom$1, async } from 'rxjs';
7
+ import { map, takeUntil, take, filter, catchError, debounceTime, startWith, distinctUntilChanged, concatMap, tap, skip, auditTime, withLatestFrom, observeOn, switchMap as switchMap$1, pairwise as pairwise$1, mergeMap, delay as delay$1 } from 'rxjs/operators';
8
8
  import { v4 } from 'uuid';
9
9
  import * as i3 from '@ngx-translate/core';
10
10
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
11
11
  import * as i2$1 from '@angular/router';
12
- import { Router, ActivationEnd, RouterModule, ActivatedRoute, NavigationError, NavigationCancel, NavigationEnd, NavigationStart } from '@angular/router';
12
+ import { Router, ActivationEnd, RouterModule, ActivatedRoute, NavigationEnd, NavigationCancel, NavigationError, NavigationStart } from '@angular/router';
13
13
  import { HttpStatusCode, HttpEventType, HttpClient, HttpParams, HTTP_INTERCEPTORS } from '@angular/common/http';
14
14
  import * as _ from 'lodash';
15
15
  import { upperCase, lowerFirst, isEqual, range, get as get$1, cloneDeep, union, isNumber as isNumber$1 } from 'lodash';
@@ -19,6 +19,7 @@ import { ComponentPortal } from '@angular/cdk/portal';
19
19
  import { OverlayPositionBuilder, Overlay } from '@angular/cdk/overlay';
20
20
  import isNumber from 'lodash/isNumber';
21
21
  import { EventSourcePolyfill } from 'event-source-polyfill';
22
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
22
23
  import { DomSanitizer } from '@angular/platform-browser';
23
24
  import * as i3$1 from 'ngx-editor';
24
25
  import { Editor, NgxEditorModule } from 'ngx-editor';
@@ -31,7 +32,6 @@ import 'moment/locale/fr';
31
32
  import 'moment/locale/it';
32
33
  import moment$1 from 'moment/moment';
33
34
  import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
34
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
35
35
  import * as i1$1 from '@ngrx/store';
36
36
  import { createAction, props, createReducer, on, createSelector, createFeatureSelector, Store, StoreModule } from '@ngrx/store';
37
37
  import { diff } from 'deep-object-diff';
@@ -7115,6 +7115,295 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
7115
7115
  }]
7116
7116
  }] });
7117
7117
 
7118
+ /**
7119
+ * Framework-wide funnel for syncing application state with the URL `?key=value` query string.
7120
+ *
7121
+ * Several Quadrel features mirror their state to the URL via per-feature adapters. Without
7122
+ * coordination they each call `router.navigate(...)` directly, which produces two failure modes:
7123
+ *
7124
+ * 1. **Race**: two navigates issued in the same JS turn cancel each other. Only the last writer
7125
+ * wins; the URL silently loses the cancelled feature's params.
7126
+ * 2. **History pollution**: a single user action that changes two features (e.g. clicking a sort
7127
+ * header resets the page index) ends up as two history entries. Browser-back only undoes one.
7128
+ *
7129
+ * `QdRouterQueryParamHubService` is the single funnel through which features run their query-param
7130
+ * reads and writes. It does three jobs:
7131
+ *
7132
+ * - **Ownership** — `claim()` / `release()` enforce one writer per param key, app-wide.
7133
+ * - **Shared reads** — `queryParams()` and `snapshotQueryParam()` expose the route state without
7134
+ * each feature needing its own `ActivatedRoute` subscription.
7135
+ * - **Coordinated writes** — `write()` buffers param updates within a microtask and serializes
7136
+ * navigations via a Promise chain, so two writes in the same JS turn become one history entry
7137
+ * and concurrent writes never race.
7138
+ *
7139
+ * The hub is feature-agnostic. Per-feature adapters (e.g. `QdTableSortRouterConnectorService`)
7140
+ * own their parsing/serialization and delegate the URL plumbing to the hub.
7141
+ *
7142
+ * ### Usage
7143
+ *
7144
+ * ```ts
7145
+ * @Injectable()
7146
+ * export class FilterRouterAdapterService {
7147
+ * private readonly _hub = inject(QdRouterQueryParamHubService);
7148
+ * private readonly _destroyRef = inject(DestroyRef);
7149
+ *
7150
+ * connect(state$: Observable<FilterState>, dispatch: (state: FilterState) => void): void {
7151
+ * if (!this._hub.isAvailable()) return;
7152
+ * if (!this._hub.claim(['filter'], this, this._destroyRef, { name: 'Filter', plural: 'filters' })) return;
7153
+ *
7154
+ * this._hub
7155
+ * .queryParams()
7156
+ * .pipe(takeUntilDestroyed(this._destroyRef))
7157
+ * .subscribe(params => dispatch(parseFilter(params['filter'])));
7158
+ *
7159
+ * state$
7160
+ * .pipe(takeUntilDestroyed(this._destroyRef))
7161
+ * .subscribe(state => this._hub.write({ filter: serializeFilter(state) }, { replaceUrl: false }));
7162
+ * }
7163
+ * }
7164
+ * ```
7165
+ */
7166
+ class QdRouterQueryParamHubService {
7167
+ /**
7168
+ * Replays whenever the router has finished a navigation (`NavigationEnd`, `NavigationCancel`
7169
+ * or `NavigationError` — every event that ends a navigation, regardless of outcome).
7170
+ * Adapters that want to delay a write until any in-flight navigation has settled can take(1)
7171
+ * on this stream before calling `write()`.
7172
+ *
7173
+ * Late subscribers receive the most recent settling event immediately, so an adapter that
7174
+ * mounts after the initial route navigation can observe the route as already-settled. The
7175
+ * first replayed tick may represent that initial route navigation rather than a user-driven
7176
+ * settling event — adapters that need to distinguish "the route just changed" should compare
7177
+ * `ActivatedRoute.snapshot` themselves.
7178
+ *
7179
+ * **When the hub is unavailable** (no `Router` / `ActivatedRoute` in the injector — see
7180
+ * `isAvailable()`) this observable never emits. Adapters that compose `navigationSettled$`
7181
+ * via operators like `switchMap` or `concat` must guard with `isAvailable()` first, otherwise
7182
+ * their pipelines hang silently.
7183
+ */
7184
+ navigationSettled$;
7185
+ _router = inject(Router, { optional: true });
7186
+ _activatedRoute = inject(ActivatedRoute, { optional: true });
7187
+ _destroyRef = inject(DestroyRef);
7188
+ _ownership = new Map();
7189
+ _ownerLabels = new WeakMap();
7190
+ _destroyClaims = new WeakMap();
7191
+ _navigationSettledSubject = new ReplaySubject(1);
7192
+ _pendingParams = {};
7193
+ _pendingReplaceUrl = false;
7194
+ _flushScheduled = false;
7195
+ _navigationChain = Promise.resolve();
7196
+ constructor() {
7197
+ this.navigationSettled$ = this._navigationSettledSubject.asObservable();
7198
+ if (!this._router || !this._activatedRoute)
7199
+ return;
7200
+ this.replayCurrentRouterStateForLateSubscribers(this._router);
7201
+ this.forwardSettlingRouterEventsToNavigationSettled(this._router);
7202
+ }
7203
+ /**
7204
+ * Returns whether the hub can sync at all. False when the consuming injector has neither a
7205
+ * `Router` nor an `ActivatedRoute` available — for example in unit tests that omit
7206
+ * `RouterTestingModule`. Adapters should bail out early when this returns false.
7207
+ */
7208
+ isAvailable() {
7209
+ return !!this._router && !!this._activatedRoute;
7210
+ }
7211
+ /**
7212
+ * Reserves exclusive write access to the given query-param keys for `owner`.
7213
+ *
7214
+ * The same `owner` may claim the same keys repeatedly without conflict — claims are idempotent
7215
+ * per owner. A different owner attempting to claim an already-claimed key fails: the hub logs
7216
+ * a `console.error` listing every contested key and returns `false`. The original owner keeps
7217
+ * the keys until it calls `release()` (or until its `DestroyRef` fires, if a `destroyRef` is
7218
+ * passed here).
7219
+ *
7220
+ * Atomicity: when the call fails, no keys are claimed, even partially. The hub checks every
7221
+ * requested key against the registry before mutating anything.
7222
+ *
7223
+ * **Pass `destroyRef` to opt into auto-release.** Without it, the hub keeps a strong reference
7224
+ * to `owner` until `release()` is called manually — a destroyed adapter that forgets to release
7225
+ * pins itself in the registry. Passing the adapter's `DestroyRef` registers an `onDestroy`
7226
+ * callback that releases exactly the keys claimed via this call (and any later claims that
7227
+ * reuse the same `destroyRef`), which is the recommended path.
7228
+ *
7229
+ * Each `destroyRef` releases only the keys it owns. If the same owner uses two different
7230
+ * `DestroyRef`s in two `claim()` calls, each fires independently and only releases its own
7231
+ * keys — keys claimed via the still-alive `destroyRef` stay reserved.
7232
+ *
7233
+ * Re-claiming with the same `destroyRef` appends to the same destroy listener — no duplicate
7234
+ * listeners are registered, even when the second claim names a different owner. Each appended
7235
+ * entry carries its own owner, so when the `destroyRef` fires the hub releases each entry's
7236
+ * keys against that entry's owner. An empty `keys` array is a no-op even when `destroyRef`
7237
+ * is provided: no listener is attached, so the hub does not pin a destroy hook that would
7238
+ * release nothing.
7239
+ *
7240
+ * @param keys The query-param keys this adapter wants to own (e.g. `['sort']`, `['page', 'size']`).
7241
+ * @param owner Stable identity of the caller — usually `this`. Used as the registry key.
7242
+ * @param destroyRef Optional. If provided, the hub releases the keys automatically when the
7243
+ * `DestroyRef` fires. Pass the adapter's `inject(DestroyRef)` to make leaks impossible.
7244
+ * @param featureLabel Optional. User-facing display label for the adapter's owning feature. The
7245
+ * hub uses it to format ownership-conflict error messages so application developers see the
7246
+ * feature names they configured (e.g. `Filter`, `Page Tabs`, `Table`) instead of internal
7247
+ * adapter class names. When omitted, the hub falls back to a generic `another adapter` phrase.
7248
+ * @returns `true` if every requested key is now owned by `owner`. `false` if at least one key
7249
+ * was already owned by a different adapter; the registry is left untouched and `destroyRef`
7250
+ * is not registered.
7251
+ */
7252
+ claim(keys, owner, destroyRef, featureLabel) {
7253
+ const conflicts = [];
7254
+ for (const key of keys) {
7255
+ const existing = this._ownership.get(key);
7256
+ if (existing && existing !== owner)
7257
+ conflicts.push(key);
7258
+ }
7259
+ if (conflicts.length > 0) {
7260
+ const existingOwner = this._ownership.get(conflicts[0]);
7261
+ const existingLabel = this._ownerLabels.get(existingOwner);
7262
+ console.error(this.formatOwnershipConflictMessage(conflicts, existingLabel));
7263
+ return false;
7264
+ }
7265
+ keys.forEach(key => this._ownership.set(key, owner));
7266
+ if (featureLabel)
7267
+ this._ownerLabels.set(owner, featureLabel);
7268
+ if (destroyRef && keys.length > 0)
7269
+ this.registerAutoRelease(owner, keys, destroyRef);
7270
+ return true;
7271
+ }
7272
+ formatOwnershipConflictMessage(conflicts, existingLabel) {
7273
+ const keysList = conflicts.map(key => `"${key}"`).join(', ');
7274
+ const paramHeader = conflicts.length === 1 ? 'Query param' : 'Query params';
7275
+ const verb = conflicts.length === 1 ? 'is' : 'are';
7276
+ const ownerPhrase = existingLabel?.name ?? 'another adapter';
7277
+ const lines = [
7278
+ `Quadrel Framework | Router Query Param Hub - ${paramHeader} ${keysList} ${verb} already owned by ${ownerPhrase}.`
7279
+ ];
7280
+ if (existingLabel) {
7281
+ lines.push(`Only one router-connected ${existingLabel.name} is allowed per view.`, `Please set connectWithRouter: false on all additional ${existingLabel.plural}.`);
7282
+ }
7283
+ else {
7284
+ lines.push("Disable the conflicting feature's URL sync or move it to a different param key.");
7285
+ }
7286
+ return lines.join('\n');
7287
+ }
7288
+ /**
7289
+ * Drops `owner`'s claim on the given keys. Keys not owned by `owner` are left untouched —
7290
+ * a foreign release call cannot steal another adapter's ownership.
7291
+ *
7292
+ * Adapters typically release in their `DestroyRef.onDestroy` callback so a destroyed component
7293
+ * frees its keys for the next adapter that mounts.
7294
+ */
7295
+ release(keys, owner) {
7296
+ keys.forEach(key => {
7297
+ if (this._ownership.get(key) === owner)
7298
+ this._ownership.delete(key);
7299
+ });
7300
+ }
7301
+ /**
7302
+ * Reactive view of `ActivatedRoute.queryParams`. Returns the empty observable if the hub is
7303
+ * not available (no Router/ActivatedRoute). Adapters subscribe here instead of injecting
7304
+ * `ActivatedRoute` themselves so the hub stays the single read funnel.
7305
+ *
7306
+ * The observable is hot — late subscribers receive the current params immediately.
7307
+ */
7308
+ queryParams() {
7309
+ return this._activatedRoute?.queryParams ?? of({});
7310
+ }
7311
+ /**
7312
+ * Synchronous read of a single query param from the latest `ActivatedRoute` snapshot.
7313
+ * Adapters use this to detect "URL already matches the desired state" and skip a redundant
7314
+ * `write()`.
7315
+ *
7316
+ * @returns The param value, or `undefined` if the param is missing or the hub is unavailable.
7317
+ */
7318
+ snapshotQueryParam(key) {
7319
+ return this._activatedRoute?.snapshot.queryParamMap.get(key) ?? undefined;
7320
+ }
7321
+ /**
7322
+ * Schedules a query-param update.
7323
+ *
7324
+ * Two coordination guarantees:
7325
+ *
7326
+ * - **Microtask batching** — every `write()` issued in the same JavaScript turn is merged into
7327
+ * a single `router.navigate(...)` call. A user action that changes two features at once
7328
+ * (e.g. sort change resets the page) becomes one history entry, not two. Note: batching is
7329
+ * per JS turn, not per logical user action — writes that fall into the next microtask after
7330
+ * a settled navigation produce a separate history entry, even when they belong to the same
7331
+ * conceptual gesture.
7332
+ * - **Serialized navigations** — flushes are chained on a Promise so a second flush only fires
7333
+ * after the previous `router.navigate` has settled. Concurrent navigates can therefore not
7334
+ * cancel each other. A rejected `router.navigate` (cancelled by a guard, redirected, or
7335
+ * failed) does not stop the chain — the next pending flush still runs. Rejections are not
7336
+ * logged here; consumers that need routing diagnostics should subscribe to `router.events`.
7337
+ *
7338
+ * Adapters are still responsible for skipping no-op writes (compare the desired value against
7339
+ * `snapshotQueryParam()` first), since the hub does not deduplicate identical values.
7340
+ *
7341
+ * Setting a param value to `undefined` removes the param from the URL via Angular Router's
7342
+ * `merge` behavior. Other params not mentioned in the call are preserved.
7343
+ *
7344
+ * @param params Partial map of query-param keys to their new values. Missing keys are not touched.
7345
+ * An empty object short-circuits and is a no-op.
7346
+ * @param options Write options — see `QdRouterQueryParamWriteOptions` for the `replaceUrl`
7347
+ * semantics and per-batch escalation rules.
7348
+ */
7349
+ write(params, options) {
7350
+ const router = this._router;
7351
+ const activatedRoute = this._activatedRoute;
7352
+ if (!router || !activatedRoute)
7353
+ return;
7354
+ if (Object.keys(params).length === 0)
7355
+ return;
7356
+ Object.assign(this._pendingParams, params);
7357
+ this._pendingReplaceUrl = this._pendingReplaceUrl || options.replaceUrl;
7358
+ if (this._flushScheduled)
7359
+ return;
7360
+ this._flushScheduled = true;
7361
+ Promise.resolve().then(() => this.flush(router, activatedRoute));
7362
+ }
7363
+ replayCurrentRouterStateForLateSubscribers(router) {
7364
+ if (router.navigated)
7365
+ this._navigationSettledSubject.next();
7366
+ }
7367
+ forwardSettlingRouterEventsToNavigationSettled(router) {
7368
+ router.events
7369
+ .pipe(filter(event => event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError), takeUntilDestroyed(this._destroyRef))
7370
+ .subscribe(() => this._navigationSettledSubject.next());
7371
+ }
7372
+ registerAutoRelease(owner, keys, destroyRef) {
7373
+ const existingClaims = this._destroyClaims.get(destroyRef);
7374
+ if (existingClaims) {
7375
+ existingClaims.push({ owner, keys });
7376
+ return;
7377
+ }
7378
+ const claims = [{ owner, keys }];
7379
+ this._destroyClaims.set(destroyRef, claims);
7380
+ destroyRef.onDestroy(() => {
7381
+ for (const claim of claims)
7382
+ this.release(claim.keys, claim.owner);
7383
+ });
7384
+ }
7385
+ flush(router, activatedRoute) {
7386
+ const params = this._pendingParams;
7387
+ const replaceUrl = this._pendingReplaceUrl;
7388
+ this._pendingParams = {};
7389
+ this._pendingReplaceUrl = false;
7390
+ this._flushScheduled = false;
7391
+ const exec = () => router.navigate([], {
7392
+ relativeTo: activatedRoute,
7393
+ queryParams: params,
7394
+ queryParamsHandling: 'merge',
7395
+ replaceUrl
7396
+ });
7397
+ this._navigationChain = this._navigationChain.catch(() => undefined).then(exec);
7398
+ }
7399
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdRouterQueryParamHubService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7400
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdRouterQueryParamHubService, providedIn: 'root' });
7401
+ }
7402
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdRouterQueryParamHubService, decorators: [{
7403
+ type: Injectable,
7404
+ args: [{ providedIn: 'root' }]
7405
+ }], ctorParameters: () => [] });
7406
+
7118
7407
  const MAX_TOOLTIP_CHARACTER = 512;
7119
7408
  const TOOLTIP_POSITIONS = [
7120
7409
  { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom' },
@@ -11418,6 +11707,8 @@ class QdDatepickerComponent {
11418
11707
  timePicker;
11419
11708
  _disabledDatesValidator;
11420
11709
  _disabledTimesValidator;
11710
+ _boundDisabledDates;
11711
+ _boundDisabledTimes;
11421
11712
  _subs = new Subscription();
11422
11713
  _onChange = () => { };
11423
11714
  _onTouched = () => { };
@@ -11429,17 +11720,17 @@ class QdDatepickerComponent {
11429
11720
  this.updateConfiguration();
11430
11721
  this.updateDisplayedDate();
11431
11722
  this.updateDisplayedDateTime();
11432
- this.validateDisabledDates();
11433
- this.validateDisabledTimes();
11434
11723
  this._subs.add(this.initOpModeSubscription());
11435
11724
  }
11436
11725
  ngOnChanges(changes) {
11437
11726
  if (changes.config) {
11438
11727
  this.updateConfiguration();
11439
- this.validateDisabledDates();
11440
- this.validateDisabledTimes();
11441
11728
  }
11442
11729
  }
11730
+ ngDoCheck() {
11731
+ this.rebindDisabledDatesValidatorIfChanged();
11732
+ this.rebindDisabledTimesValidatorIfChanged();
11733
+ }
11443
11734
  ngOnDestroy() {
11444
11735
  this._subs.unsubscribe();
11445
11736
  }
@@ -11610,6 +11901,20 @@ class QdDatepickerComponent {
11610
11901
  this.displayedDateTime = QdDateAdapter.formatToDateTimeLocaleString(currentValue, this.config?.enableSeconds);
11611
11902
  this.displayedTime = QdDateAdapter.formatToTimeLocaleString(currentValue, this.config?.enableSeconds);
11612
11903
  }
11904
+ rebindDisabledDatesValidatorIfChanged() {
11905
+ const current = this.config?.disabledDates;
11906
+ if (current === this._boundDisabledDates)
11907
+ return;
11908
+ this._boundDisabledDates = current;
11909
+ this.validateDisabledDates();
11910
+ }
11911
+ rebindDisabledTimesValidatorIfChanged() {
11912
+ const current = this.config?.timePicker?.disabledTimes;
11913
+ if (current === this._boundDisabledTimes)
11914
+ return;
11915
+ this._boundDisabledTimes = current;
11916
+ this.validateDisabledTimes();
11917
+ }
11613
11918
  validateDisabledDates() {
11614
11919
  if (!this.control)
11615
11920
  return;
@@ -20913,99 +21218,86 @@ function unescape(escapedString) {
20913
21218
  return escapedString.replace(new RegExp(`\\\\(${SERIALIZATION_SYMBOLS.map(s => '\\' + s).join('|')})`, 'g'), '$1');
20914
21219
  }
20915
21220
 
20916
- // @ts-strict-ignore
21221
+ const FILTER_PARAM_NAME = 'filter';
21222
+ const OWNED_PARAMS$4 = [FILTER_PARAM_NAME];
21223
+ const FEATURE_LABEL$4 = { name: 'Filter', plural: 'filters' };
21224
+ /**
21225
+ * Per-view adapter that syncs `QdFilterComponent` selection with the URL `?filter=` query
21226
+ * param via `QdRouterQueryParamHubService`. Reads/parses on activation, writes on selection
21227
+ * change. Coordination, ownership, and write batching are delegated to the hub — see its
21228
+ * JSDoc for details. Behavior contracts live in `filter-router-connector.service.spec.ts`
21229
+ * and `router-query-param-hub.integration.cy.ts`.
21230
+ */
20917
21231
  class QdFilterRouterConnectorService {
20918
- filterService = inject(QdFilterService);
20919
- router = inject(Router, { optional: true });
20920
- activatedRoute = inject(ActivatedRoute, { optional: true });
21232
+ _filterService = inject(QdFilterService);
21233
+ _hub = inject(QdRouterQueryParamHubService);
21234
+ _destroyRef = inject(DestroyRef);
20921
21235
  _connectedFilter;
20922
- _activatedRouteSubscription;
20923
- _filterUrlParameterSubscription;
20924
- _navigationEnded$;
21236
+ _readSubscription;
21237
+ _writeSubscription;
20925
21238
  _activatedRouteCheckedSubject = new ReplaySubject(1);
20926
21239
  _activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
20927
21240
  constructor() {
20928
- if (!this.router || !this.activatedRoute)
20929
- return;
20930
- const navigationPending$ = new ReplaySubject(1);
20931
- this._navigationEnded$ = navigationPending$.pipe(filter(navigationPending => !navigationPending));
20932
- if (this.router.navigated)
20933
- navigationPending$.next(false);
20934
- this.router.events.subscribe(event => {
20935
- switch (event.constructor) {
20936
- case NavigationStart:
20937
- navigationPending$.next(true);
20938
- break;
20939
- case NavigationEnd:
20940
- case NavigationCancel:
20941
- case NavigationError:
20942
- navigationPending$.next(false);
20943
- break;
20944
- }
20945
- });
21241
+ this._destroyRef.onDestroy(() => this.tearDownConnection());
20946
21242
  }
20947
21243
  connectFilterWithRouter(filterComponent) {
20948
- if (filterComponent.filterData?.connectWithRouter === false || !this.router || !this.activatedRoute) {
21244
+ if (filterComponent.filterData?.connectWithRouter === false || !this._hub.isAvailable())
20949
21245
  return of(false);
20950
- }
20951
- if (this._connectedFilter) {
20952
- console.error('Quadrel Framework | QdFilter - More than one filter with enabled "connectWithRouter" config is not allowed. ' +
20953
- 'Please set connectWithRouter to false except for one filter.');
21246
+ if (this._connectedFilter === filterComponent)
21247
+ return this._activatedRouteChecked$;
21248
+ if (!this._hub.claim(OWNED_PARAMS$4, this, this._destroyRef, FEATURE_LABEL$4))
20954
21249
  return of(false);
20955
- }
20956
21250
  this._connectedFilter = filterComponent;
20957
- this.setFilterSelectionFromUrl();
20958
- this.setUrlOnFilterSelection();
21251
+ this._activatedRouteCheckedSubject = new ReplaySubject(1);
21252
+ this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
21253
+ this.subscribeToUrlChanges();
21254
+ this.subscribeToFilterChanges();
20959
21255
  return this._activatedRouteChecked$;
20960
21256
  }
20961
- setFilterSelectionFromUrl() {
20962
- this._activatedRouteSubscription = this._navigationEnded$
20963
- .pipe(switchMap(() => combineLatest([
20964
- this.activatedRoute.queryParams,
20965
- this.filterService.selectFilterDataById$(this._connectedFilter.filterId)
20966
- ])), take$1(1))
20967
- .subscribe(([queryParams, filterData]) => {
20968
- if (typeof queryParams.filter === 'string') {
20969
- this.filterService.setFilterSelection(this._connectedFilter.filterId, {
20970
- ...filterData
20971
- .map(category => ({ [category.category]: [] }))
20972
- .reduce((accumulator, emptyCategory) => ({ ...accumulator, ...emptyCategory }), {}),
20973
- ...parseFilterUrlParameter(queryParams.filter)
21257
+ tearDownConnection() {
21258
+ this._connectedFilter = undefined;
21259
+ this._readSubscription?.unsubscribe();
21260
+ this._writeSubscription?.unsubscribe();
21261
+ }
21262
+ subscribeToUrlChanges() {
21263
+ let isFirstEmit = true;
21264
+ this._readSubscription = this._hub.navigationSettled$
21265
+ .pipe(switchMap(() => this._hub.queryParams()), map(queryParams => queryParams[FILTER_PARAM_NAME]), distinctUntilChanged(), switchMap(rawFilter => this._filterService.selectFilterDataById$(this._connectedFilter.filterId).pipe(take(1), map(filterData => ({ rawFilter, filterData })))), takeUntilDestroyed(this._destroyRef))
21266
+ .subscribe(({ rawFilter, filterData }) => {
21267
+ const emptySelection = filterData
21268
+ .map(category => ({ [category.category]: [] }))
21269
+ .reduce((accumulator, emptyCategory) => ({ ...accumulator, ...emptyCategory }), {});
21270
+ if (typeof rawFilter === 'string') {
21271
+ this._filterService.setFilterSelection(this._connectedFilter.filterId, {
21272
+ ...emptySelection,
21273
+ ...parseFilterUrlParameter(rawFilter)
20974
21274
  });
20975
21275
  }
20976
- this._activatedRouteCheckedSubject.next(true);
21276
+ else if (!isFirstEmit) {
21277
+ this._filterService.setFilterSelection(this._connectedFilter.filterId, emptySelection);
21278
+ }
21279
+ if (isFirstEmit) {
21280
+ this._activatedRouteCheckedSubject.next(true);
21281
+ isFirstEmit = false;
21282
+ }
20977
21283
  });
20978
21284
  }
20979
- setUrlOnFilterSelection() {
20980
- this._filterUrlParameterSubscription = this.filterService
21285
+ subscribeToFilterChanges() {
21286
+ this._writeSubscription = this._filterService
20981
21287
  .getFilterUrlParameter$(this._connectedFilter.filterId)
20982
- .pipe(switchMap(filterUrlParameter => this._navigationEnded$.pipe(take$1(1), map(() => filterUrlParameter))))
21288
+ .pipe(distinctUntilChanged(), switchMap(filterUrlParameter => this._hub.navigationSettled$.pipe(take(1), map(() => filterUrlParameter))), takeUntilDestroyed(this._destroyRef))
20983
21289
  .subscribe(filterUrlParameter => {
20984
- this.router.navigate([], {
20985
- relativeTo: this.activatedRoute,
20986
- queryParams: {
20987
- filter: filterUrlParameter
20988
- },
20989
- queryParamsHandling: 'merge',
20990
- replaceUrl: true
20991
- });
21290
+ const target = filterUrlParameter || undefined;
21291
+ if (this._hub.snapshotQueryParam(FILTER_PARAM_NAME) === target)
21292
+ return;
21293
+ this._hub.write({ [FILTER_PARAM_NAME]: target }, { replaceUrl: false });
20992
21294
  });
20993
21295
  }
20994
- disconnectFilterFromRouter(filterComponent) {
20995
- if (this._connectedFilter !== filterComponent)
20996
- return;
20997
- this._connectedFilter = null;
20998
- this._activatedRouteSubscription?.unsubscribe();
20999
- this._filterUrlParameterSubscription?.unsubscribe();
21000
- }
21001
21296
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
21002
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService, providedIn: 'root' });
21297
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService });
21003
21298
  }
21004
21299
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService, decorators: [{
21005
- type: Injectable,
21006
- args: [{
21007
- providedIn: 'root'
21008
- }]
21300
+ type: Injectable
21009
21301
  }], ctorParameters: () => [] });
21010
21302
 
21011
21303
  class QdDependentFiltersService {
@@ -21145,7 +21437,7 @@ class QdFilterComponent {
21145
21437
  _filterDataSubject = new BehaviorSubject(undefined);
21146
21438
  _hasPreselectionSubject = new ReplaySubject(1);
21147
21439
  _categoriesSubject = new ReplaySubject(1);
21148
- _filterConnectedWithRouterSubject = new ReplaySubject();
21440
+ _filterConnectedWithRouterSubject = new ReplaySubject(1);
21149
21441
  _filterConnectedWithRouter$ = this._filterConnectedWithRouterSubject.asObservable();
21150
21442
  set hasPreselection(hasPreselection) {
21151
21443
  this._hasPreselectionSubject.next(hasPreselection);
@@ -21183,7 +21475,6 @@ class QdFilterComponent {
21183
21475
  }
21184
21476
  }
21185
21477
  ngOnDestroy() {
21186
- this.filterRouterConnectorService.disconnectFilterFromRouter(this);
21187
21478
  this.dependentFiltersService.destroy();
21188
21479
  this._queryStringSubscription?.unsubscribe();
21189
21480
  this._postBodySubscription?.unsubscribe();
@@ -21318,11 +21609,11 @@ class QdFilterComponent {
21318
21609
  });
21319
21610
  }
21320
21611
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
21321
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdFilterComponent, isStandalone: false, selector: "qd-filter", inputs: { filterData: "filterData", testId: ["data-test-id", "testId"] }, outputs: { queryStringOutput: "queryStringOutput", postBodyOutput: "postBodyOutput", valueChange: "valueChange" }, host: { properties: { "attr.data-test-id": "this.dataTestId" }, classAttribute: "qd-filter" }, providers: [QdDependentFiltersService], viewQueries: [{ propertyName: "filterCategory", predicate: i0.forwardRef(() => QdFilterCategoryComponent), descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"(categories$ | async)?.length > 0\">\n <ng-container\n *ngFor=\"let category of categories$ | async; let last = last; let length = count; let categoryIndex = index\"\n >\n <qd-filter-category\n [filterId]=\"filterId\"\n [isInstantFiltering]=\"isInstantFiltering\"\n [categoryIndex]=\"categoryIndex\"\n [lastCategory]=\"last\"\n [data-test-id]=\"testId + '-category-' + categoryIndex + '-' + category\"\n ></qd-filter-category>\n </ng-container>\n\n <div [class]=\"'qd-filter__action-buttons'\" *ngIf=\"!isInstantFiltering\">\n <button\n qdButton\n qdButtonLink\n [disabled]=\"disableFilterButton$ | async\"\n (click)=\"clickFilter()\"\n [color]=\"'primary'\"\n [data-test-id]=\"testId + '-submit-button'\"\n >\n {{ \"i18n.qd.container.toolbar.filter.filter\" | translate }}\n </button>\n\n <button\n qdButton\n qdButtonLink\n [disabled]=\"disableResetButton$ | async\"\n (click)=\"resetFilter()\"\n [color]=\"'secondary'\"\n [data-test-id]=\"testId + '-reset-button'\"\n >\n {{\n ((hasPreselection$ | async)\n ? \"i18n.qd.container.toolbar.filter.backToPreSelect\"\n : \"i18n.qd.container.toolbar.filter.reset\"\n ) | translate\n }}\n </button>\n </div>\n</ng-container>\n", styles: [":host{position:relative;display:flex;flex-wrap:wrap}:host .qd-filter__action-buttons{display:flex;flex-wrap:wrap-reverse}:host ::ng-deep .qd-button{position:relative;margin-bottom:1rem}:host ::ng-deep .qd-button:last-child{margin-left:1rem}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonLinkDirective, selector: "button[qdButtonLink], a[qdButtonLink], button[qd-button-link]" }, { kind: "component", type: QdFilterCategoryComponent, selector: "qd-filter-category", inputs: ["filterId", "isInstantFiltering", "categoryIndex", "lastCategory", "data-test-id"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
21612
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdFilterComponent, isStandalone: false, selector: "qd-filter", inputs: { filterData: "filterData", testId: ["data-test-id", "testId"] }, outputs: { queryStringOutput: "queryStringOutput", postBodyOutput: "postBodyOutput", valueChange: "valueChange" }, host: { properties: { "attr.data-test-id": "this.dataTestId" }, classAttribute: "qd-filter" }, providers: [QdDependentFiltersService, QdFilterRouterConnectorService], viewQueries: [{ propertyName: "filterCategory", predicate: i0.forwardRef(() => QdFilterCategoryComponent), descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"(categories$ | async)?.length > 0\">\n <ng-container\n *ngFor=\"let category of categories$ | async; let last = last; let length = count; let categoryIndex = index\"\n >\n <qd-filter-category\n [filterId]=\"filterId\"\n [isInstantFiltering]=\"isInstantFiltering\"\n [categoryIndex]=\"categoryIndex\"\n [lastCategory]=\"last\"\n [data-test-id]=\"testId + '-category-' + categoryIndex + '-' + category\"\n ></qd-filter-category>\n </ng-container>\n\n <div [class]=\"'qd-filter__action-buttons'\" *ngIf=\"!isInstantFiltering\">\n <button\n qdButton\n qdButtonLink\n [disabled]=\"disableFilterButton$ | async\"\n (click)=\"clickFilter()\"\n [color]=\"'primary'\"\n [data-test-id]=\"testId + '-submit-button'\"\n >\n {{ \"i18n.qd.container.toolbar.filter.filter\" | translate }}\n </button>\n\n <button\n qdButton\n qdButtonLink\n [disabled]=\"disableResetButton$ | async\"\n (click)=\"resetFilter()\"\n [color]=\"'secondary'\"\n [data-test-id]=\"testId + '-reset-button'\"\n >\n {{\n ((hasPreselection$ | async)\n ? \"i18n.qd.container.toolbar.filter.backToPreSelect\"\n : \"i18n.qd.container.toolbar.filter.reset\"\n ) | translate\n }}\n </button>\n </div>\n</ng-container>\n", styles: [":host{position:relative;display:flex;flex-wrap:wrap}:host .qd-filter__action-buttons{display:flex;flex-wrap:wrap-reverse}:host ::ng-deep .qd-button{position:relative;margin-bottom:1rem}:host ::ng-deep .qd-button:last-child{margin-left:1rem}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonLinkDirective, selector: "button[qdButtonLink], a[qdButtonLink], button[qd-button-link]" }, { kind: "component", type: QdFilterCategoryComponent, selector: "qd-filter-category", inputs: ["filterId", "isInstantFiltering", "categoryIndex", "lastCategory", "data-test-id"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
21322
21613
  }
21323
21614
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterComponent, decorators: [{
21324
21615
  type: Component,
21325
- args: [{ selector: 'qd-filter', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'qd-filter' }, providers: [QdDependentFiltersService], standalone: false, template: "<ng-container *ngIf=\"(categories$ | async)?.length > 0\">\n <ng-container\n *ngFor=\"let category of categories$ | async; let last = last; let length = count; let categoryIndex = index\"\n >\n <qd-filter-category\n [filterId]=\"filterId\"\n [isInstantFiltering]=\"isInstantFiltering\"\n [categoryIndex]=\"categoryIndex\"\n [lastCategory]=\"last\"\n [data-test-id]=\"testId + '-category-' + categoryIndex + '-' + category\"\n ></qd-filter-category>\n </ng-container>\n\n <div [class]=\"'qd-filter__action-buttons'\" *ngIf=\"!isInstantFiltering\">\n <button\n qdButton\n qdButtonLink\n [disabled]=\"disableFilterButton$ | async\"\n (click)=\"clickFilter()\"\n [color]=\"'primary'\"\n [data-test-id]=\"testId + '-submit-button'\"\n >\n {{ \"i18n.qd.container.toolbar.filter.filter\" | translate }}\n </button>\n\n <button\n qdButton\n qdButtonLink\n [disabled]=\"disableResetButton$ | async\"\n (click)=\"resetFilter()\"\n [color]=\"'secondary'\"\n [data-test-id]=\"testId + '-reset-button'\"\n >\n {{\n ((hasPreselection$ | async)\n ? \"i18n.qd.container.toolbar.filter.backToPreSelect\"\n : \"i18n.qd.container.toolbar.filter.reset\"\n ) | translate\n }}\n </button>\n </div>\n</ng-container>\n", styles: [":host{position:relative;display:flex;flex-wrap:wrap}:host .qd-filter__action-buttons{display:flex;flex-wrap:wrap-reverse}:host ::ng-deep .qd-button{position:relative;margin-bottom:1rem}:host ::ng-deep .qd-button:last-child{margin-left:1rem}\n"] }]
21616
+ args: [{ selector: 'qd-filter', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'qd-filter' }, providers: [QdDependentFiltersService, QdFilterRouterConnectorService], standalone: false, template: "<ng-container *ngIf=\"(categories$ | async)?.length > 0\">\n <ng-container\n *ngFor=\"let category of categories$ | async; let last = last; let length = count; let categoryIndex = index\"\n >\n <qd-filter-category\n [filterId]=\"filterId\"\n [isInstantFiltering]=\"isInstantFiltering\"\n [categoryIndex]=\"categoryIndex\"\n [lastCategory]=\"last\"\n [data-test-id]=\"testId + '-category-' + categoryIndex + '-' + category\"\n ></qd-filter-category>\n </ng-container>\n\n <div [class]=\"'qd-filter__action-buttons'\" *ngIf=\"!isInstantFiltering\">\n <button\n qdButton\n qdButtonLink\n [disabled]=\"disableFilterButton$ | async\"\n (click)=\"clickFilter()\"\n [color]=\"'primary'\"\n [data-test-id]=\"testId + '-submit-button'\"\n >\n {{ \"i18n.qd.container.toolbar.filter.filter\" | translate }}\n </button>\n\n <button\n qdButton\n qdButtonLink\n [disabled]=\"disableResetButton$ | async\"\n (click)=\"resetFilter()\"\n [color]=\"'secondary'\"\n [data-test-id]=\"testId + '-reset-button'\"\n >\n {{\n ((hasPreselection$ | async)\n ? \"i18n.qd.container.toolbar.filter.backToPreSelect\"\n : \"i18n.qd.container.toolbar.filter.reset\"\n ) | translate\n }}\n </button>\n </div>\n</ng-container>\n", styles: [":host{position:relative;display:flex;flex-wrap:wrap}:host .qd-filter__action-buttons{display:flex;flex-wrap:wrap-reverse}:host ::ng-deep .qd-button{position:relative;margin-bottom:1rem}:host ::ng-deep .qd-button:last-child{margin-left:1rem}\n"] }]
21326
21617
  }], propDecorators: { filterData: [{
21327
21618
  type: Input
21328
21619
  }], testId: [{
@@ -21990,94 +22281,88 @@ function searchReducer(state, action) {
21990
22281
  }
21991
22282
 
21992
22283
  const PHRASE_PRESELECT_SEPARATOR = '|';
22284
+ const SEARCH_PARAM_NAME = 'search';
22285
+ const OWNED_PARAMS$3 = [SEARCH_PARAM_NAME];
22286
+ const FEATURE_LABEL$3 = { name: 'Search', plural: 'searches' };
22287
+ /**
22288
+ * Per-view adapter that syncs `QdSearchComponent` phrase and preselection with the URL
22289
+ * `?search=` query param via `QdRouterQueryParamHubService`. Reads/parses on activation,
22290
+ * writes when the search service emits a submit. Coordination, ownership, and write
22291
+ * batching are delegated to the hub — see its JSDoc for details. Behavior contracts live
22292
+ * in `search-router-connector.service.spec.ts` and
22293
+ * `router-query-param-hub.integration.cy.ts`.
22294
+ */
21993
22295
  class QdSearchRouterConnectorService {
21994
- router = inject(Router, { optional: true });
21995
- activatedRoute = inject(ActivatedRoute, { optional: true });
22296
+ _hub = inject(QdRouterQueryParamHubService);
22297
+ _destroyRef = inject(DestroyRef);
21996
22298
  _connectedSearch;
21997
- _activatedRouteSubscription;
21998
- _searchUrlParameterSubscription;
21999
- _navigationEnded$;
22299
+ _readSubscription;
22300
+ _writeSubscription;
22000
22301
  _activatedRouteCheckedSubject = new ReplaySubject(1);
22001
22302
  _activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
22002
22303
  constructor() {
22003
- if (!this.router || !this.activatedRoute)
22004
- return;
22005
- const navigationPending$ = new ReplaySubject(1);
22006
- this._navigationEnded$ = navigationPending$.pipe(filter(navigationPending => !navigationPending));
22007
- if (this.router.navigated)
22008
- navigationPending$.next(false);
22009
- this.router.events.subscribe(event => {
22010
- switch (event.constructor) {
22011
- case NavigationStart:
22012
- navigationPending$.next(true);
22013
- break;
22014
- case NavigationEnd:
22015
- case NavigationCancel:
22016
- case NavigationError:
22017
- navigationPending$.next(false);
22018
- break;
22019
- }
22020
- });
22304
+ this._destroyRef.onDestroy(() => this.tearDownConnection());
22021
22305
  }
22022
22306
  connectSearchWithRouter(searchComponent) {
22023
- if (searchComponent.configData?.connectWithRouter !== true || !this.router || !this.activatedRoute) {
22307
+ if (searchComponent.configData?.connectWithRouter !== true || !this._hub.isAvailable())
22024
22308
  return of(false);
22025
- }
22026
- if (this._connectedSearch) {
22027
- console.error('Quadrel Framework | QdSearch - More than one search with enabled "connectWithRouter" config is not allowed. ' +
22028
- 'Please set connectWithRouter to false except for one search.');
22309
+ if (this._connectedSearch === searchComponent)
22310
+ return this._activatedRouteChecked$;
22311
+ if (!this._hub.claim(OWNED_PARAMS$3, this, this._destroyRef, FEATURE_LABEL$3))
22029
22312
  return of(false);
22030
- }
22031
22313
  this._connectedSearch = searchComponent;
22032
- this.setSearchPhraseFromUrl();
22033
- this.setUrlOnSearch();
22314
+ this._activatedRouteCheckedSubject = new ReplaySubject(1);
22315
+ this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
22316
+ this.subscribeToUrlChanges();
22317
+ this.subscribeToSearchChanges();
22034
22318
  return this._activatedRouteChecked$;
22035
22319
  }
22036
- setSearchPhraseFromUrl() {
22037
- this._activatedRouteSubscription = this._navigationEnded$
22038
- .pipe(switchMap(() => this.activatedRoute.queryParams), take$1(1))
22039
- .subscribe((queryParams) => {
22040
- if (typeof queryParams.search === 'string') {
22041
- const [phrase, preSelect] = queryParams.search.split(this.getNotEscapedSplitRegExp()).map(this.unescape);
22042
- if (this._connectedSearch) {
22043
- this._connectedSearch.search = phrase;
22044
- this._connectedSearch.preSelect = preSelect || '';
22045
- }
22320
+ tearDownConnection() {
22321
+ this._connectedSearch = undefined;
22322
+ this._readSubscription?.unsubscribe();
22323
+ this._writeSubscription?.unsubscribe();
22324
+ }
22325
+ subscribeToUrlChanges() {
22326
+ let isFirstEmit = true;
22327
+ this._readSubscription = this._hub.navigationSettled$
22328
+ .pipe(switchMap(() => this._hub.queryParams()), map(queryParams => queryParams[SEARCH_PARAM_NAME]), distinctUntilChanged(), takeUntilDestroyed(this._destroyRef))
22329
+ .subscribe(rawSearch => {
22330
+ if (typeof rawSearch === 'string' && this._connectedSearch) {
22331
+ const [phrase, preSelect] = rawSearch
22332
+ .split(this.getNotEscapedSplitRegExp())
22333
+ .map(value => this.unescape(value));
22334
+ this._connectedSearch.search = phrase;
22335
+ this._connectedSearch.preSelect = preSelect || '';
22336
+ }
22337
+ else if (!isFirstEmit && this._connectedSearch) {
22338
+ this._connectedSearch.search = '';
22339
+ this._connectedSearch.preSelect = '';
22046
22340
  }
22047
- this._activatedRouteCheckedSubject.next(true);
22341
+ if (isFirstEmit) {
22342
+ this._activatedRouteCheckedSubject.next(true);
22343
+ isFirstEmit = false;
22344
+ }
22345
+ });
22346
+ }
22347
+ subscribeToSearchChanges() {
22348
+ this._writeSubscription = this._connectedSearch.searchService.searchPostBody$.pipe(switchMap(searchData => this._hub.navigationSettled$.pipe(take(1), map(() => searchData))), takeUntilDestroyed(this._destroyRef)).subscribe(({ phrase, preSelect }) => {
22349
+ const serialized = this.escape(phrase) + (preSelect ? PHRASE_PRESELECT_SEPARATOR + this.escape(preSelect) : '');
22350
+ const target = serialized || undefined;
22351
+ if (this._hub.snapshotQueryParam(SEARCH_PARAM_NAME) === target)
22352
+ return;
22353
+ this._hub.write({ [SEARCH_PARAM_NAME]: target }, { replaceUrl: false });
22048
22354
  });
22049
22355
  }
22050
22356
  getNotEscapedSplitRegExp() {
22051
22357
  return new RegExp('(?<!\\\\)\\' + PHRASE_PRESELECT_SEPARATOR);
22052
22358
  }
22053
- setUrlOnSearch() {
22054
- this._searchUrlParameterSubscription = this._connectedSearch?.searchService.searchPostBody$
22055
- .pipe(switchMap(searchData => this._navigationEnded$.pipe(take$1(1), map(() => searchData))))
22056
- .subscribe(({ phrase, preSelect }) => {
22057
- this.router.navigate([], {
22058
- relativeTo: this.activatedRoute,
22059
- queryParams: {
22060
- search: this.escape(phrase) + (preSelect ? PHRASE_PRESELECT_SEPARATOR + this.escape(preSelect) : '')
22061
- },
22062
- queryParamsHandling: 'merge',
22063
- replaceUrl: true
22064
- });
22065
- });
22066
- }
22067
- escape(string) {
22068
- if (!string)
22359
+ escape(value) {
22360
+ if (!value)
22069
22361
  return '';
22070
- return string.replace(new RegExp(`(\\${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '\\$1');
22071
- }
22072
- unescape(escapedString) {
22073
- return escapedString.replace(new RegExp(`\\\\(${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '$1');
22362
+ return value.replace(new RegExp(`(\\${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '\\$1');
22074
22363
  }
22075
- disconnectSearchFromRouter(searchComponent) {
22076
- if (this._connectedSearch !== searchComponent)
22077
- return;
22078
- this._connectedSearch = null;
22079
- this._activatedRouteSubscription?.unsubscribe();
22080
- this._searchUrlParameterSubscription?.unsubscribe();
22364
+ unescape(escapedValue) {
22365
+ return escapedValue.replace(new RegExp(`\\\\(${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '$1');
22081
22366
  }
22082
22367
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdSearchRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
22083
22368
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdSearchRouterConnectorService });
@@ -22274,7 +22559,7 @@ class QdSearchComponent {
22274
22559
  _search = '';
22275
22560
  _preSelect = '';
22276
22561
  _destroyed$ = new Subject();
22277
- _searchConnectedWithRouterSubject = new ReplaySubject();
22562
+ _searchConnectedWithRouterSubject = new ReplaySubject(1);
22278
22563
  _searchConnectedWithRouter$ = this._searchConnectedWithRouterSubject.asObservable();
22279
22564
  get inputConfig() {
22280
22565
  return {
@@ -22357,7 +22642,6 @@ class QdSearchComponent {
22357
22642
  }
22358
22643
  }
22359
22644
  ngOnDestroy() {
22360
- this.searchRouterConnectorService.disconnectSearchFromRouter(this);
22361
22645
  this._destroyed$.next();
22362
22646
  this._destroyed$.complete();
22363
22647
  }
@@ -22704,6 +22988,9 @@ class QdTableStoreSelectorService {
22704
22988
  return sort.find(entry => entry.column === column)?.direction ?? QdSortDirection.NONE;
22705
22989
  });
22706
22990
  }
22991
+ tableSort() {
22992
+ return createSelector(this.selectTables, tables => this.getTable(tables)?.sort);
22993
+ }
22707
22994
  selectIsLoading() {
22708
22995
  return createSelector(this.selectTables, tables => {
22709
22996
  const { requestState } = this.getTable(tables) || {};
@@ -22890,6 +23177,9 @@ class QdTableStoreService {
22890
23177
  columnSortDirection$(column) {
22891
23178
  return this.store.select(this.tableStoreSelectorService.columnSortDirection(column));
22892
23179
  }
23180
+ tableSort$() {
23181
+ return this.store.select(this.tableStoreSelectorService.tableSort());
23182
+ }
22893
23183
  selectIsLoading$() {
22894
23184
  return this.store.select(this.tableStoreSelectorService.selectIsLoading());
22895
23185
  }
@@ -23360,6 +23650,158 @@ var QdPaginatorDirection;
23360
23650
  QdPaginatorDirection[QdPaginatorDirection["LastPage"] = 3] = "LastPage";
23361
23651
  })(QdPaginatorDirection || (QdPaginatorDirection = {}));
23362
23652
 
23653
+ const PAGE_PARAM_NAME = 'page';
23654
+ const SIZE_PARAM_NAME = 'size';
23655
+ const OWNED_PARAMS$2 = [PAGE_PARAM_NAME, SIZE_PARAM_NAME];
23656
+ const FEATURE_LABEL$2 = { name: 'Table', plural: 'tables' };
23657
+ const SIZE_SANITY_MAX = 1000;
23658
+ /**
23659
+ * Per-view adapter that syncs `QdTablePaginatorComponent` page and size with the URL
23660
+ * `?page=` and `?size=` query params via `QdRouterQueryParamHubService`. Applies the URL
23661
+ * synchronously on connect (so cross-adapter store reducers cannot stomp on the deep-linked
23662
+ * page), then keeps store and URL in sync. Coordination, ownership, and write batching are
23663
+ * delegated to the hub — see its JSDoc for details. Behavior contracts live in
23664
+ * `pagination-router-connector.service.spec.ts` and `router-query-param-hub.integration.cy.ts`.
23665
+ *
23666
+ * Internal invariant: relies on `pageChangeInfo$()` and `totalCount$()` being
23667
+ * BehaviorSubject-backed selectors that emit their current value on subscribe. If either
23668
+ * becomes async (delay/debounce), the synchronous snapshot in `applyInitialUrlSynchronously`
23669
+ * stays at its initial default silently — the dispatch would always fire and the table would
23670
+ * flicker to "0 elements" on disconnect/reconnect cycles.
23671
+ */
23672
+ class QdTablePaginationRouterConnectorService {
23673
+ _hub = inject(QdRouterQueryParamHubService);
23674
+ _destroyRef = inject(DestroyRef);
23675
+ _connection;
23676
+ _readSubscription;
23677
+ _writeSubscription;
23678
+ _activatedRouteCheckedSubject = new ReplaySubject(1);
23679
+ _activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
23680
+ _hasInitialUrlBeenSet = false;
23681
+ constructor() {
23682
+ this._destroyRef.onDestroy(() => this.tearDownConnection());
23683
+ }
23684
+ connectPaginationWithRouter(paginator, tableStoreService) {
23685
+ if (!paginator.shouldConnectWithRouter() || !this._hub.isAvailable())
23686
+ return of(false);
23687
+ if (!this._hub.claim(OWNED_PARAMS$2, this, this._destroyRef, FEATURE_LABEL$2))
23688
+ return of(false);
23689
+ this._connection = {
23690
+ paginator: paginator,
23691
+ tableStoreService: tableStoreService
23692
+ };
23693
+ this._hasInitialUrlBeenSet = false;
23694
+ this._activatedRouteCheckedSubject = new ReplaySubject(1);
23695
+ this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
23696
+ this.applyInitialUrlSynchronously();
23697
+ this.subscribeToUrlChanges();
23698
+ this.subscribeToStoreChanges();
23699
+ return this._activatedRouteChecked$;
23700
+ }
23701
+ applyInitialUrlSynchronously() {
23702
+ const { tableStoreService, paginator } = this._connection;
23703
+ const params = this.parseUrlParams({
23704
+ [PAGE_PARAM_NAME]: this._hub.snapshotQueryParam(PAGE_PARAM_NAME),
23705
+ [SIZE_PARAM_NAME]: this._hub.snapshotQueryParam(SIZE_PARAM_NAME)
23706
+ });
23707
+ const pageSize = params.size ?? paginator.getPageSizeDefault();
23708
+ const pageIndex = params.page !== undefined ? params.page - 1 : 0;
23709
+ let currentInfo;
23710
+ let currentTotalCount = 0;
23711
+ tableStoreService
23712
+ .pageChangeInfo$()
23713
+ .pipe(take(1))
23714
+ .subscribe(info => (currentInfo = info));
23715
+ tableStoreService
23716
+ .totalCount$()
23717
+ .pipe(take(1))
23718
+ .subscribe(value => (currentTotalCount = value));
23719
+ if (currentInfo && currentInfo.pageIndex === pageIndex && currentInfo.pageSize === pageSize)
23720
+ return;
23721
+ tableStoreService.setPageParams(pageIndex, pageSize, currentTotalCount);
23722
+ }
23723
+ tearDownConnection() {
23724
+ this._connection = undefined;
23725
+ this._hasInitialUrlBeenSet = false;
23726
+ this._readSubscription?.unsubscribe();
23727
+ this._writeSubscription?.unsubscribe();
23728
+ }
23729
+ subscribeToUrlChanges() {
23730
+ const { tableStoreService } = this._connection;
23731
+ let isFirstEmit = true;
23732
+ this._readSubscription = this._hub.navigationSettled$
23733
+ .pipe(switchMap(() => this._hub.queryParams()), map(queryParams => this.parseUrlParams(queryParams)), distinctUntilChanged((a, b) => a.page === b.page && a.size === b.size), switchMap(params => combineLatest([tableStoreService.pageChangeInfo$(), tableStoreService.totalCount$()]).pipe(take(1), map(([currentInfo, totalCount]) => ({ params, currentInfo, totalCount })))))
23734
+ .subscribe(({ params, currentInfo, totalCount }) => {
23735
+ const target = this.toStoreState(params);
23736
+ const isChanged = !currentInfo || currentInfo.pageIndex !== target.pageIndex || currentInfo.pageSize !== target.pageSize;
23737
+ if (isChanged) {
23738
+ tableStoreService.setPageParams(target.pageIndex, target.pageSize, totalCount);
23739
+ }
23740
+ if (isFirstEmit) {
23741
+ this._activatedRouteCheckedSubject.next(true);
23742
+ isFirstEmit = false;
23743
+ }
23744
+ });
23745
+ }
23746
+ subscribeToStoreChanges() {
23747
+ const { tableStoreService } = this._connection;
23748
+ this._writeSubscription = tableStoreService
23749
+ .pageChangeInfo$()
23750
+ .pipe(filter((info) => info !== undefined), distinctUntilChanged((a, b) => a.pageIndex === b.pageIndex && a.pageSize === b.pageSize), switchMap(info => this._hub.navigationSettled$.pipe(take(1), map(() => info))))
23751
+ .subscribe(info => this.writeUrl(info.pageIndex, info.pageSize));
23752
+ }
23753
+ writeUrl(pageIndex, pageSize) {
23754
+ const replaceUrl = !this._hasInitialUrlBeenSet;
23755
+ this._hasInitialUrlBeenSet = true;
23756
+ const targetPage = String(pageIndex + 1);
23757
+ const targetSize = String(pageSize);
23758
+ const urlAlreadyMatches = this._hub.snapshotQueryParam(PAGE_PARAM_NAME) === targetPage &&
23759
+ this._hub.snapshotQueryParam(SIZE_PARAM_NAME) === targetSize;
23760
+ if (urlAlreadyMatches)
23761
+ return;
23762
+ this._hub.write({ [PAGE_PARAM_NAME]: targetPage, [SIZE_PARAM_NAME]: targetSize }, { replaceUrl });
23763
+ }
23764
+ parseUrlParams(queryParams) {
23765
+ const result = {};
23766
+ const rawPage = queryParams[PAGE_PARAM_NAME];
23767
+ if (typeof rawPage === 'string' && this.isValidPositiveIntegerString(rawPage)) {
23768
+ result.page = Number(rawPage);
23769
+ }
23770
+ const rawSize = queryParams[SIZE_PARAM_NAME];
23771
+ if (typeof rawSize === 'string' &&
23772
+ this.isValidPositiveIntegerString(rawSize) &&
23773
+ this.isValidSize(Number(rawSize))) {
23774
+ result.size = Number(rawSize);
23775
+ }
23776
+ return result;
23777
+ }
23778
+ isValidPositiveIntegerString(raw) {
23779
+ return /^[1-9]\d*$/.test(raw);
23780
+ }
23781
+ isValidSize(value) {
23782
+ const pageSizes = this.getConfiguredPageSizes();
23783
+ if (pageSizes && pageSizes.length > 0)
23784
+ return pageSizes.includes(value);
23785
+ return value <= SIZE_SANITY_MAX;
23786
+ }
23787
+ getConfiguredPageSizes() {
23788
+ const pagination = this._connection?.paginator.config?.pagination;
23789
+ return typeof pagination === 'object' ? pagination.pageSizes : undefined;
23790
+ }
23791
+ toStoreState(params) {
23792
+ const { paginator } = this._connection;
23793
+ return {
23794
+ pageIndex: params.page !== undefined ? params.page - 1 : 0,
23795
+ pageSize: params.size ?? paginator.getPageSizeDefault()
23796
+ };
23797
+ }
23798
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTablePaginationRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
23799
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTablePaginationRouterConnectorService });
23800
+ }
23801
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTablePaginationRouterConnectorService, decorators: [{
23802
+ type: Injectable
23803
+ }], ctorParameters: () => [] });
23804
+
23363
23805
  class QdScrollingService {
23364
23806
  scrollIntoViewIfNeeded(element, borderDirection = 'top', targetBorderDistance = 0) {
23365
23807
  this.scrollIntoView(element, borderDirection, targetBorderDistance, true);
@@ -23588,6 +24030,7 @@ const PAGE_SIZE_DEFAULT = 25;
23588
24030
  class QdTablePaginatorComponent {
23589
24031
  tableDataResolver = inject(QD_TABLE_DATA_RESOLVER_TOKEN, { optional: true });
23590
24032
  tableStoreService = inject(QdTableStoreService);
24033
+ routerConnector = inject(QdTablePaginationRouterConnectorService);
23591
24034
  /**
23592
24035
  * @description Configuration Model for Qd-Table.
23593
24036
  */
@@ -23624,11 +24067,38 @@ class QdTablePaginatorComponent {
23624
24067
  this.totalCount$ = this.tableStoreService.totalCount$();
23625
24068
  this.selectedRowsCount$ = this.tableStoreService.selectedRows$().pipe(map(ids => ids?.length ?? 0));
23626
24069
  if (this.isConfigValid())
23627
- this.initPagination();
24070
+ this.startPagination();
23628
24071
  }
23629
24072
  ngOnDestroy() {
24073
+ this._destroyed$.next(null);
23630
24074
  this._destroyed$.complete();
23631
24075
  }
24076
+ /**
24077
+ * @description Whether this paginator should sync its state with the URL. Reads
24078
+ * `pagination.connectWithRouter` (default `false` — URL sync is opt-in and
24079
+ * requires an explicit `pagination: { connectWithRouter: true }`).
24080
+ *
24081
+ * Used by `QdTablePaginationRouterConnectorService` to decide whether to claim
24082
+ * the router-singleton for this paginator.
24083
+ */
24084
+ shouldConnectWithRouter() {
24085
+ const pagination = this.config?.pagination;
24086
+ if (typeof pagination !== 'object')
24087
+ return false;
24088
+ return pagination.connectWithRouter === true;
24089
+ }
24090
+ /**
24091
+ * @description The effective default page size for this paginator. Resolves
24092
+ * `pagination.pageSizeDefault` first, then falls back to the first entry of
24093
+ * `pagination.pageSizes`, finally to the framework constant `PAGE_SIZE_DEFAULT`.
24094
+ */
24095
+ getPageSizeDefault() {
24096
+ if (typeof this.config?.pagination !== 'object')
24097
+ return PAGE_SIZE_DEFAULT;
24098
+ const { pageSizeDefault, pageSizes } = this.config.pagination;
24099
+ const pageSize = pageSizes && pageSizes.length ? pageSizes[0] : PAGE_SIZE_DEFAULT;
24100
+ return pageSizeDefault || pageSize;
24101
+ }
23632
24102
  navigateToPage(direction) {
23633
24103
  if (!this.paginatorButtons)
23634
24104
  return;
@@ -23649,16 +24119,22 @@ class QdTablePaginatorComponent {
23649
24119
  }
23650
24120
  return true;
23651
24121
  }
24122
+ startPagination() {
24123
+ if (!this.shouldConnectWithRouter()) {
24124
+ this.initPagination();
24125
+ return;
24126
+ }
24127
+ this.routerConnector
24128
+ .connectPaginationWithRouter(this, this.tableStoreService)
24129
+ .pipe(takeUntil(this._destroyed$), first())
24130
+ .subscribe(connected => {
24131
+ if (!connected)
24132
+ this.initPagination();
24133
+ });
24134
+ }
23652
24135
  initPagination() {
23653
24136
  this.tableStoreService.setupPagination(this.getPageSizeDefault());
23654
24137
  }
23655
- getPageSizeDefault() {
23656
- if (typeof this.config?.pagination !== 'object')
23657
- return PAGE_SIZE_DEFAULT;
23658
- const { pageSizeDefault, pageSizes } = this.config.pagination;
23659
- const pageSize = pageSizes && pageSizes.length ? pageSizes[0] : PAGE_SIZE_DEFAULT;
23660
- return pageSizeDefault || pageSize;
23661
- }
23662
24138
  calculatePageNumber(direction, totalCount, pageSize, currentPage) {
23663
24139
  switch (direction) {
23664
24140
  case QdPaginatorDirection.NextPage: {
@@ -23677,11 +24153,11 @@ class QdTablePaginatorComponent {
23677
24153
  }
23678
24154
  }
23679
24155
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTablePaginatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
23680
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdTablePaginatorComponent, isStandalone: false, selector: "qd-table-paginator", inputs: { config: "config", testId: ["data-test-id", "testId"] }, viewQueries: [{ propertyName: "paginatorButtons", first: true, predicate: ["paginatorButtons"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"hasData$ | async\">\n <qd-table-paginator-page-size [pageSizes]=\"pageSizes\" *ngIf=\"pageSizes.length > 0\"></qd-table-paginator-page-size>\n <span class=\"paginator-current-page\" *ngIf=\"currentPage$ | async as page\">\n {{ page.start }}\u2014{{ page.end }}\n {{ \"i18n.qd.table.pagination.of\" | translate }}\n {{ page.totalCount }}\n {{\n page.totalCount === 1\n ? (\"i18n.qd.table.pagination.entry\" | translate)\n : (\"i18n.qd.table.pagination.entries\" | translate)\n }}\n </span>\n <span\n *ngIf=\"{ count: (selectedRowsCount$ | async) ?? 0 } as ctx\"\n [hidden]=\"ctx.count === 0 || config?.selection?.type !== 'multiSelect'\"\n class=\"paginator-selection-count\"\n [attr.data-test-id]=\"testId + '-paginator-selection-count'\"\n >\n {{\n ctx.count === 1\n ? (\"i18n.qd.table.pagination.selectedSingular\" | translate)\n : (\"i18n.qd.table.pagination.selectedPlural\" | translate : { count: ctx.count })\n }}\n </span>\n <span class=\"paginator-buttons\" data-test=\"paginator-buttons\" #paginatorButtons qdScrollToPagination>\n <button\n *ngIf=\"config?.pagination['hasFirstLastPageNavigation']\"\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-first-page'\"\n (click)=\"navigateToPage(pageNav.FirstPage)\"\n [disabled]=\"(canPageBackward$ | async) === false\"\n >\n <qd-icon [icon]=\"'pageFirst1'\"></qd-icon>\n </button>\n <button\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-backward'\"\n (click)=\"navigateToPage(pageNav.PreviousPage)\"\n [disabled]=\"(canPageBackward$ | async) === false\"\n >\n <qd-icon [icon]=\"'triangleLeftSolid'\"></qd-icon>\n </button>\n <button\n class=\"paginator-button forward\"\n [attr.data-test-id]=\"testId + '-paginator-forward'\"\n (click)=\"navigateToPage(pageNav.NextPage)\"\n [disabled]=\"(canPageForward$ | async) === false\"\n >\n <qd-icon [icon]=\"'triangleRightSolid'\"></qd-icon>\n </button>\n <button\n *ngIf=\"config?.pagination['hasFirstLastPageNavigation']\"\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-last-page'\"\n (click)=\"navigateToPage(pageNav.LastPage)\"\n [disabled]=\"(canPageForward$ | async) === false\"\n >\n <qd-icon [icon]=\"'pageLast'\"></qd-icon>\n </button>\n </span>\n</ng-container>\n", styles: [":host{color:#454545;font-size:.875rem;font-weight:400;line-height:1.3125rem;display:flex;flex-direction:row;align-items:center;justify-content:space-between;background-color:#fff}.paginator-current-page{margin-left:1rem}.paginator-selection-count{margin-right:1rem;margin-left:1rem}.paginator-buttons{margin-left:auto;text-align:right;white-space:nowrap}button.paginator-button{width:2.75rem;height:2.75rem;border-left:.0625rem solid rgb(229,229,229);background-color:unset;color:#454545;font-size:1.5rem}button.paginator-button:disabled{color:#b4b4b4;cursor:default}button.paginator-button:not(:disabled):hover{background-color:#e5e5e5;color:#171717}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdIconComponent, selector: "qd-icon", inputs: ["icon"] }, { kind: "directive", type: QdScrollToPaginationDirective, selector: "[qdScrollToPagination]" }, { kind: "component", type: QdTablePaginatorPageSizeComponent, selector: "qd-table-paginator-page-size", inputs: ["pageSizes"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
24156
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdTablePaginatorComponent, isStandalone: false, selector: "qd-table-paginator", inputs: { config: "config", testId: ["data-test-id", "testId"] }, providers: [QdTablePaginationRouterConnectorService], viewQueries: [{ propertyName: "paginatorButtons", first: true, predicate: ["paginatorButtons"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"hasData$ | async\">\n <qd-table-paginator-page-size [pageSizes]=\"pageSizes\" *ngIf=\"pageSizes.length > 0\"></qd-table-paginator-page-size>\n <span class=\"paginator-current-page\" *ngIf=\"currentPage$ | async as page\">\n {{ page.start }}\u2014{{ page.end }}\n {{ \"i18n.qd.table.pagination.of\" | translate }}\n {{ page.totalCount }}\n {{\n page.totalCount === 1\n ? (\"i18n.qd.table.pagination.entry\" | translate)\n : (\"i18n.qd.table.pagination.entries\" | translate)\n }}\n </span>\n <span\n *ngIf=\"{ count: (selectedRowsCount$ | async) ?? 0 } as ctx\"\n [hidden]=\"ctx.count === 0 || config?.selection?.type !== 'multiSelect'\"\n class=\"paginator-selection-count\"\n [attr.data-test-id]=\"testId + '-paginator-selection-count'\"\n >\n {{\n ctx.count === 1\n ? (\"i18n.qd.table.pagination.selectedSingular\" | translate)\n : (\"i18n.qd.table.pagination.selectedPlural\" | translate : { count: ctx.count })\n }}\n </span>\n <span class=\"paginator-buttons\" data-test=\"paginator-buttons\" #paginatorButtons qdScrollToPagination>\n <button\n *ngIf=\"config?.pagination['hasFirstLastPageNavigation']\"\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-first-page'\"\n (click)=\"navigateToPage(pageNav.FirstPage)\"\n [disabled]=\"(canPageBackward$ | async) === false\"\n >\n <qd-icon [icon]=\"'pageFirst1'\"></qd-icon>\n </button>\n <button\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-backward'\"\n (click)=\"navigateToPage(pageNav.PreviousPage)\"\n [disabled]=\"(canPageBackward$ | async) === false\"\n >\n <qd-icon [icon]=\"'triangleLeftSolid'\"></qd-icon>\n </button>\n <button\n class=\"paginator-button forward\"\n [attr.data-test-id]=\"testId + '-paginator-forward'\"\n (click)=\"navigateToPage(pageNav.NextPage)\"\n [disabled]=\"(canPageForward$ | async) === false\"\n >\n <qd-icon [icon]=\"'triangleRightSolid'\"></qd-icon>\n </button>\n <button\n *ngIf=\"config?.pagination['hasFirstLastPageNavigation']\"\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-last-page'\"\n (click)=\"navigateToPage(pageNav.LastPage)\"\n [disabled]=\"(canPageForward$ | async) === false\"\n >\n <qd-icon [icon]=\"'pageLast'\"></qd-icon>\n </button>\n </span>\n</ng-container>\n", styles: [":host{color:#454545;font-size:.875rem;font-weight:400;line-height:1.3125rem;display:flex;flex-direction:row;align-items:center;justify-content:space-between;background-color:#fff}.paginator-current-page{margin-left:1rem}.paginator-selection-count{margin-right:1rem;margin-left:1rem}.paginator-buttons{margin-left:auto;text-align:right;white-space:nowrap}button.paginator-button{width:2.75rem;height:2.75rem;border-left:.0625rem solid rgb(229,229,229);background-color:unset;color:#454545;font-size:1.5rem}button.paginator-button:disabled{color:#b4b4b4;cursor:default}button.paginator-button:not(:disabled):hover{background-color:#e5e5e5;color:#171717}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdIconComponent, selector: "qd-icon", inputs: ["icon"] }, { kind: "directive", type: QdScrollToPaginationDirective, selector: "[qdScrollToPagination]" }, { kind: "component", type: QdTablePaginatorPageSizeComponent, selector: "qd-table-paginator-page-size", inputs: ["pageSizes"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
23681
24157
  }
23682
24158
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTablePaginatorComponent, decorators: [{
23683
24159
  type: Component,
23684
- args: [{ selector: 'qd-table-paginator', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ng-container *ngIf=\"hasData$ | async\">\n <qd-table-paginator-page-size [pageSizes]=\"pageSizes\" *ngIf=\"pageSizes.length > 0\"></qd-table-paginator-page-size>\n <span class=\"paginator-current-page\" *ngIf=\"currentPage$ | async as page\">\n {{ page.start }}\u2014{{ page.end }}\n {{ \"i18n.qd.table.pagination.of\" | translate }}\n {{ page.totalCount }}\n {{\n page.totalCount === 1\n ? (\"i18n.qd.table.pagination.entry\" | translate)\n : (\"i18n.qd.table.pagination.entries\" | translate)\n }}\n </span>\n <span\n *ngIf=\"{ count: (selectedRowsCount$ | async) ?? 0 } as ctx\"\n [hidden]=\"ctx.count === 0 || config?.selection?.type !== 'multiSelect'\"\n class=\"paginator-selection-count\"\n [attr.data-test-id]=\"testId + '-paginator-selection-count'\"\n >\n {{\n ctx.count === 1\n ? (\"i18n.qd.table.pagination.selectedSingular\" | translate)\n : (\"i18n.qd.table.pagination.selectedPlural\" | translate : { count: ctx.count })\n }}\n </span>\n <span class=\"paginator-buttons\" data-test=\"paginator-buttons\" #paginatorButtons qdScrollToPagination>\n <button\n *ngIf=\"config?.pagination['hasFirstLastPageNavigation']\"\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-first-page'\"\n (click)=\"navigateToPage(pageNav.FirstPage)\"\n [disabled]=\"(canPageBackward$ | async) === false\"\n >\n <qd-icon [icon]=\"'pageFirst1'\"></qd-icon>\n </button>\n <button\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-backward'\"\n (click)=\"navigateToPage(pageNav.PreviousPage)\"\n [disabled]=\"(canPageBackward$ | async) === false\"\n >\n <qd-icon [icon]=\"'triangleLeftSolid'\"></qd-icon>\n </button>\n <button\n class=\"paginator-button forward\"\n [attr.data-test-id]=\"testId + '-paginator-forward'\"\n (click)=\"navigateToPage(pageNav.NextPage)\"\n [disabled]=\"(canPageForward$ | async) === false\"\n >\n <qd-icon [icon]=\"'triangleRightSolid'\"></qd-icon>\n </button>\n <button\n *ngIf=\"config?.pagination['hasFirstLastPageNavigation']\"\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-last-page'\"\n (click)=\"navigateToPage(pageNav.LastPage)\"\n [disabled]=\"(canPageForward$ | async) === false\"\n >\n <qd-icon [icon]=\"'pageLast'\"></qd-icon>\n </button>\n </span>\n</ng-container>\n", styles: [":host{color:#454545;font-size:.875rem;font-weight:400;line-height:1.3125rem;display:flex;flex-direction:row;align-items:center;justify-content:space-between;background-color:#fff}.paginator-current-page{margin-left:1rem}.paginator-selection-count{margin-right:1rem;margin-left:1rem}.paginator-buttons{margin-left:auto;text-align:right;white-space:nowrap}button.paginator-button{width:2.75rem;height:2.75rem;border-left:.0625rem solid rgb(229,229,229);background-color:unset;color:#454545;font-size:1.5rem}button.paginator-button:disabled{color:#b4b4b4;cursor:default}button.paginator-button:not(:disabled):hover{background-color:#e5e5e5;color:#171717}\n"] }]
24160
+ args: [{ selector: 'qd-table-paginator', providers: [QdTablePaginationRouterConnectorService], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ng-container *ngIf=\"hasData$ | async\">\n <qd-table-paginator-page-size [pageSizes]=\"pageSizes\" *ngIf=\"pageSizes.length > 0\"></qd-table-paginator-page-size>\n <span class=\"paginator-current-page\" *ngIf=\"currentPage$ | async as page\">\n {{ page.start }}\u2014{{ page.end }}\n {{ \"i18n.qd.table.pagination.of\" | translate }}\n {{ page.totalCount }}\n {{\n page.totalCount === 1\n ? (\"i18n.qd.table.pagination.entry\" | translate)\n : (\"i18n.qd.table.pagination.entries\" | translate)\n }}\n </span>\n <span\n *ngIf=\"{ count: (selectedRowsCount$ | async) ?? 0 } as ctx\"\n [hidden]=\"ctx.count === 0 || config?.selection?.type !== 'multiSelect'\"\n class=\"paginator-selection-count\"\n [attr.data-test-id]=\"testId + '-paginator-selection-count'\"\n >\n {{\n ctx.count === 1\n ? (\"i18n.qd.table.pagination.selectedSingular\" | translate)\n : (\"i18n.qd.table.pagination.selectedPlural\" | translate : { count: ctx.count })\n }}\n </span>\n <span class=\"paginator-buttons\" data-test=\"paginator-buttons\" #paginatorButtons qdScrollToPagination>\n <button\n *ngIf=\"config?.pagination['hasFirstLastPageNavigation']\"\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-first-page'\"\n (click)=\"navigateToPage(pageNav.FirstPage)\"\n [disabled]=\"(canPageBackward$ | async) === false\"\n >\n <qd-icon [icon]=\"'pageFirst1'\"></qd-icon>\n </button>\n <button\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-backward'\"\n (click)=\"navigateToPage(pageNav.PreviousPage)\"\n [disabled]=\"(canPageBackward$ | async) === false\"\n >\n <qd-icon [icon]=\"'triangleLeftSolid'\"></qd-icon>\n </button>\n <button\n class=\"paginator-button forward\"\n [attr.data-test-id]=\"testId + '-paginator-forward'\"\n (click)=\"navigateToPage(pageNav.NextPage)\"\n [disabled]=\"(canPageForward$ | async) === false\"\n >\n <qd-icon [icon]=\"'triangleRightSolid'\"></qd-icon>\n </button>\n <button\n *ngIf=\"config?.pagination['hasFirstLastPageNavigation']\"\n class=\"paginator-button\"\n [attr.data-test-id]=\"testId + '-paginator-last-page'\"\n (click)=\"navigateToPage(pageNav.LastPage)\"\n [disabled]=\"(canPageForward$ | async) === false\"\n >\n <qd-icon [icon]=\"'pageLast'\"></qd-icon>\n </button>\n </span>\n</ng-container>\n", styles: [":host{color:#454545;font-size:.875rem;font-weight:400;line-height:1.3125rem;display:flex;flex-direction:row;align-items:center;justify-content:space-between;background-color:#fff}.paginator-current-page{margin-left:1rem}.paginator-selection-count{margin-right:1rem;margin-left:1rem}.paginator-buttons{margin-left:auto;text-align:right;white-space:nowrap}button.paginator-button{width:2.75rem;height:2.75rem;border-left:.0625rem solid rgb(229,229,229);background-color:unset;color:#454545;font-size:1.5rem}button.paginator-button:disabled{color:#b4b4b4;cursor:default}button.paginator-button:not(:disabled):hover{background-color:#e5e5e5;color:#171717}\n"] }]
23685
24161
  }], propDecorators: { config: [{
23686
24162
  type: Input
23687
24163
  }], testId: [{
@@ -23692,6 +24168,161 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
23692
24168
  args: ['paginatorButtons']
23693
24169
  }] } });
23694
24170
 
24171
+ const SORT_PARAM_NAME = 'sort';
24172
+ const OWNED_PARAMS$1 = [SORT_PARAM_NAME];
24173
+ const FEATURE_LABEL$1 = { name: 'Table', plural: 'tables' };
24174
+ const SEGMENT_SEPARATOR = ',';
24175
+ const COLUMN_DIRECTION_SEPARATOR = '.';
24176
+ /**
24177
+ * Per-view adapter that syncs `QdTableComponent` sort state with the URL `?sort=` query
24178
+ * param via `QdRouterQueryParamHubService`. Reads/parses on activation (single segment
24179
+ * only; multi-segment URLs use the first valid entry and warn), writes when the store's
24180
+ * `tableSort$` selector emits. Coordination, ownership, and write batching are delegated
24181
+ * to the hub — see its JSDoc for details. Behavior contracts live in
24182
+ * `sort-router-connector.service.spec.ts` and `router-query-param-hub.integration.cy.ts`.
24183
+ *
24184
+ * Internal invariant: relies on `tableSort$()` being a BehaviorSubject-backed selector that
24185
+ * emits its current value on subscribe. The URL-read pipeline pairs each segments emission
24186
+ * with the latest sort snapshot via `withLatestFrom`; if the selector ever becomes async
24187
+ * (delay/debounce), `withLatestFrom` drops the outer emission until the snapshot arrives
24188
+ * and URL-driven sort changes are silently lost.
24189
+ */
24190
+ class QdTableSortRouterConnectorService {
24191
+ _hub = inject(QdRouterQueryParamHubService);
24192
+ _destroyRef = inject(DestroyRef);
24193
+ _connection;
24194
+ _readSubscription;
24195
+ _writeSubscription;
24196
+ _activatedRouteCheckedSubject = new ReplaySubject(1);
24197
+ _activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
24198
+ _hasInitialUrlBeenSet = false;
24199
+ _hasWarnedMultiSegment = false;
24200
+ constructor() {
24201
+ this._destroyRef.onDestroy(() => this.tearDownConnection());
24202
+ }
24203
+ connectSortWithRouter(table, tableStoreService) {
24204
+ if (!this.shouldConnect(table) || !this._hub.isAvailable())
24205
+ return of(false);
24206
+ if (!this._hub.claim(OWNED_PARAMS$1, this, this._destroyRef, FEATURE_LABEL$1))
24207
+ return of(false);
24208
+ this._connection = {
24209
+ table: table,
24210
+ tableStoreService: tableStoreService,
24211
+ configuredColumns: table.config.columns
24212
+ };
24213
+ this._hasInitialUrlBeenSet = false;
24214
+ this._hasWarnedMultiSegment = false;
24215
+ this._activatedRouteCheckedSubject = new ReplaySubject(1);
24216
+ this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
24217
+ this.subscribeToUrlChanges();
24218
+ this.subscribeToStoreChanges();
24219
+ return this._activatedRouteChecked$;
24220
+ }
24221
+ tearDownConnection() {
24222
+ this._connection = undefined;
24223
+ this._hasInitialUrlBeenSet = false;
24224
+ this._hasWarnedMultiSegment = false;
24225
+ this._readSubscription?.unsubscribe();
24226
+ this._writeSubscription?.unsubscribe();
24227
+ }
24228
+ subscribeToUrlChanges() {
24229
+ const { tableStoreService } = this._connection;
24230
+ let isFirstEmit = true;
24231
+ this._readSubscription = this._hub.navigationSettled$
24232
+ .pipe(switchMap(() => this._hub.queryParams()), map(queryParams => (typeof queryParams[SORT_PARAM_NAME] === 'string' ? queryParams[SORT_PARAM_NAME] : '')), distinctUntilChanged(), map(raw => this.parseRawSortValue(raw)), withLatestFrom(tableStoreService.tableSort$()))
24233
+ .subscribe(([segments, currentSort]) => {
24234
+ if (segments.length === 1) {
24235
+ const [{ column, direction }] = segments;
24236
+ if (!this.isSameAsCurrentSort(currentSort, column, direction)) {
24237
+ tableStoreService.setSort(column, direction);
24238
+ }
24239
+ }
24240
+ if (isFirstEmit) {
24241
+ this._activatedRouteCheckedSubject.next(true);
24242
+ isFirstEmit = false;
24243
+ }
24244
+ });
24245
+ }
24246
+ isSameAsCurrentSort(currentSort, column, direction) {
24247
+ if (!currentSort)
24248
+ return false;
24249
+ const active = currentSort.filter(entry => entry.direction !== QdSortDirection.NONE);
24250
+ return active.length === 1 && active[0].column === column && active[0].direction === direction;
24251
+ }
24252
+ subscribeToStoreChanges() {
24253
+ const { tableStoreService } = this._connection;
24254
+ this._writeSubscription = tableStoreService
24255
+ .tableSort$()
24256
+ .pipe(filter((sort) => Array.isArray(sort)), map(sort => this.serializeSort(sort)), distinctUntilChanged(), switchMap(serialized => this._hub.navigationSettled$.pipe(take(1), map(() => serialized))))
24257
+ .subscribe(serialized => this.writeUrl(serialized));
24258
+ }
24259
+ writeUrl(serialized) {
24260
+ const replaceUrl = !this._hasInitialUrlBeenSet;
24261
+ this._hasInitialUrlBeenSet = true;
24262
+ if (this._hub.snapshotQueryParam(SORT_PARAM_NAME) === serialized)
24263
+ return;
24264
+ this._hub.write({ [SORT_PARAM_NAME]: serialized }, { replaceUrl });
24265
+ }
24266
+ shouldConnect(table) {
24267
+ const sortConfig = table.config.sort;
24268
+ if (sortConfig === true || sortConfig === undefined)
24269
+ return false;
24270
+ return sortConfig.connectWithRouter === true;
24271
+ }
24272
+ parseRawSortValue(raw) {
24273
+ if (raw.length === 0)
24274
+ return [];
24275
+ const rawSegments = raw.split(SEGMENT_SEPARATOR);
24276
+ const validSegments = rawSegments
24277
+ .map(segment => this.parseSegment(segment))
24278
+ .filter((segment) => segment !== undefined);
24279
+ if (validSegments.length > 1) {
24280
+ if (!this._hasWarnedMultiSegment) {
24281
+ console.warn('Quadrel Framework | QdTable - Multi-column sort URL is not yet supported. Using first valid segment only.');
24282
+ this._hasWarnedMultiSegment = true;
24283
+ }
24284
+ return [validSegments[0]];
24285
+ }
24286
+ return validSegments;
24287
+ }
24288
+ parseSegment(segment) {
24289
+ if (segment.length === 0)
24290
+ return undefined;
24291
+ const separatorIndex = segment.indexOf(COLUMN_DIRECTION_SEPARATOR);
24292
+ if (separatorIndex <= 0 || separatorIndex === segment.length - 1)
24293
+ return undefined;
24294
+ const column = segment.slice(0, separatorIndex);
24295
+ const directionRaw = segment.slice(separatorIndex + 1).toLowerCase();
24296
+ if (!this.isValidColumn(column))
24297
+ return undefined;
24298
+ if (directionRaw === 'asc')
24299
+ return { column, direction: QdSortDirection.ASC };
24300
+ if (directionRaw === 'desc')
24301
+ return { column, direction: QdSortDirection.DESC };
24302
+ return undefined;
24303
+ }
24304
+ isValidColumn(column) {
24305
+ const config = this._connection?.configuredColumns.find(c => c.column === column);
24306
+ return !!config && config.sort !== undefined && config.sort.isDisabled !== true;
24307
+ }
24308
+ serializeSort(sort) {
24309
+ const active = sort.filter((entry) => entry.direction === QdSortDirection.ASC || entry.direction === QdSortDirection.DESC);
24310
+ if (active.length === 0)
24311
+ return undefined;
24312
+ return active
24313
+ .map(entry => `${entry.column}${COLUMN_DIRECTION_SEPARATOR}${this.directionToString(entry.direction)}`)
24314
+ .join(SEGMENT_SEPARATOR);
24315
+ }
24316
+ directionToString(direction) {
24317
+ return direction === QdSortDirection.ASC ? 'asc' : 'desc';
24318
+ }
24319
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTableSortRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
24320
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTableSortRouterConnectorService });
24321
+ }
24322
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTableSortRouterConnectorService, decorators: [{
24323
+ type: Injectable
24324
+ }], ctorParameters: () => [] });
24325
+
23695
24326
  class QdTableRowActionsSecondaryMenuComponent {
23696
24327
  actionsService = inject(QdTableSecondaryActionsService);
23697
24328
  tableStore = inject(QdTableStoreService);
@@ -24307,6 +24938,7 @@ class QdTableComponent {
24307
24938
  breakpointService = inject(QdBreakpointService);
24308
24939
  resolverService = inject(QdTableResolverService);
24309
24940
  confirmationDialogService = inject(QdConfirmationDialogOpenerService);
24941
+ sortRouterConnector = inject(QdTableSortRouterConnectorService);
24310
24942
  /**
24311
24943
  * Configuration of the table. The generic type specifies the column definition. <br />
24312
24944
  *
@@ -24414,6 +25046,7 @@ class QdTableComponent {
24414
25046
  this.tableStoreService.initTableState(this._data, this.hasResolver, this._connectors, this.hasPagination);
24415
25047
  this.tableStoreService.updateTableStateRecentSecondaryAction(undefined);
24416
25048
  this.tableStoreService.setupSort(this.config.columns);
25049
+ this.sortRouterConnector.connectSortWithRouter(this, this.tableStoreService).pipe(take(1)).subscribe();
24417
25050
  this.resolverService.init(this.config.refreshOnLanguageChange, this.hasPagination);
24418
25051
  this.data$ = this.tableStoreService.tableDataEntries$();
24419
25052
  this.initializeResponsiveRowService();
@@ -24613,7 +25246,8 @@ class QdTableComponent {
24613
25246
  QdTableRowSelectionService,
24614
25247
  QdTableFillingWidthService,
24615
25248
  QdTableResolverService,
24616
- QdTableExternalActionResultService
25249
+ QdTableExternalActionResultService,
25250
+ QdTableSortRouterConnectorService
24617
25251
  ], viewQueries: [{ propertyName: "paginator", first: true, predicate: QdTablePaginatorComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<table [class]=\"'qd-table__table'\">\n <tr qd-table-head [config]=\"config\" [data-test-id]=\"testId\"></tr>\n <tbody qd-table-body [config]=\"config\" [data]=\"(data$ | async) ?? []\" [data-test-id]=\"testId\"></tbody>\n</table>\n\n<qd-table-paginator [config]=\"config\" [data-test-id]=\"testId\" *ngIf=\"hasPagination\"></qd-table-paginator>\n<qd-table-empty-state *ngIf=\"hasEmptyStateView$ | async\" [config]=\"config.emptyStateView\"></qd-table-empty-state>\n", styles: [":host{display:inline-flex;flex-direction:column}:host .qd-table__table{background:#fff;border-spacing:0;color:#171717;font-size:.875rem;font-weight:400;line-height:2.5rem}:host .qd-table__head,:host .qd-table__body{vertical-align:center}:host .qd-table__head{background:#e5e5e5;text-align:left}:host .qd-table__head ::ng-deep .qd-table__head-cell{font-weight:600}:host .qd-table__body{color:#171717}:host ::ng-deep .qd-table__head-cell,:host ::ng-deep .qd-table__body-cell{padding:.125rem 1rem 0;vertical-align:top}:host ::ng-deep .qd-table__head-cell--selection,:host ::ng-deep .qd-table__body-cell--selection{width:1.875rem;padding-top:.1875rem;text-align:center}:host ::ng-deep .qd-table__head-cell--actions-inline-menu,:host ::ng-deep .qd-table__body-cell--actions-inline-menu{padding-top:.125rem;vertical-align:top}:host.main-column-fills-width.table-has-remaining-width:not(.table-has-right-aligned-filling-column) ::ng-deep th.main-column,:host.main-column-fills-width.table-has-remaining-width:not(.table-has-right-aligned-filling-column) ::ng-deep td.main-column{width:100%}:host.main-column-fills-width.table-has-remaining-width ::ng-deep th:not(.main-column),:host.main-column-fills-width.table-has-remaining-width ::ng-deep td:not(.main-column){white-space:nowrap}:host.last-column-fills-width.table-has-remaining-width:not(.table-has-right-aligned-filling-column) ::ng-deep th.last-column,:host.last-column-fills-width.table-has-remaining-width:not(.table-has-right-aligned-filling-column) ::ng-deep td.last-column{width:100%}:host.last-column-fills-width.table-has-remaining-width ::ng-deep th:not(.last-column),:host.last-column-fills-width.table-has-remaining-width ::ng-deep td:not(.last-column){white-space:nowrap}:host.harmonized{width:100%}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdTableBodyComponent, selector: "[qd-table-body]", inputs: ["config", "data", "data-test-id"] }, { kind: "component", type: QdTableEmptyStateComponent, selector: "qd-table-empty-state", inputs: ["config"] }, { kind: "component", type: QdTableHeadComponent, selector: "[qd-table-head]", inputs: ["config", "data-test-id"] }, { kind: "component", type: QdTablePaginatorComponent, selector: "qd-table-paginator", inputs: ["config", "data-test-id"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
24618
25252
  }
24619
25253
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdTableComponent, decorators: [{
@@ -24627,7 +25261,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
24627
25261
  QdTableRowSelectionService,
24628
25262
  QdTableFillingWidthService,
24629
25263
  QdTableResolverService,
24630
- QdTableExternalActionResultService
25264
+ QdTableExternalActionResultService,
25265
+ QdTableSortRouterConnectorService
24631
25266
  ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
24632
25267
  '[class.qd-table]': 'true',
24633
25268
  '[class.main-column-fills-width]': 'whichColumnFillsWidth === "main"',
@@ -28576,6 +29211,149 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
28576
29211
  args: ['data-test-id']
28577
29212
  }] } });
28578
29213
 
29214
+ const TAB_PARAM_NAME = 'tab';
29215
+ const OWNED_PARAMS = [TAB_PARAM_NAME];
29216
+ const FEATURE_LABEL = { name: 'Page Tabs', plural: 'page tabs' };
29217
+ /**
29218
+ * Per-view adapter that syncs `QdPageTabsComponent` selection with the URL `?tab=` query
29219
+ * param via `QdRouterQueryParamHubService`. Reads on activation and selects the matching
29220
+ * tab (with fallback for unknown/disabled names), writes on `selectionChange`. Coordination,
29221
+ * ownership, and write batching are delegated to the hub — see its JSDoc for details.
29222
+ * Behavior contracts live in `page-tabs-router-connector.service.spec.ts` and
29223
+ * `router-query-param-hub.integration.cy.ts`.
29224
+ *
29225
+ * Note: this is the only connector that writes synchronously without gating on
29226
+ * `navigationSettled$`. Required for the deep-link case where `ngAfterViewInit` fires
29227
+ * before the initial NavigationEnd is replayed — gating there would hang the click-driven
29228
+ * write pipeline (see commit 57f0a271a).
29229
+ *
29230
+ * Internal invariant: when the requested tab is unknown or disabled, `selectFallbackTab()`
29231
+ * writes the URL explicitly. CdkStepper does not emit `selectionChange` if the fallback is
29232
+ * already the selected step, so without the explicit write deep-links like `?tab=phantom`
29233
+ * would leave a stale param in the URL.
29234
+ */
29235
+ class QdPageTabsRouterConnectorService {
29236
+ _hub = inject(QdRouterQueryParamHubService);
29237
+ _destroyRef = inject(DestroyRef);
29238
+ _connectedComponent;
29239
+ _readSubscription;
29240
+ _writeSubscription;
29241
+ _activatedRouteCheckedSubject = new ReplaySubject(1);
29242
+ _activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
29243
+ _hasInitialUrlBeenSet = false;
29244
+ constructor() {
29245
+ this._destroyRef.onDestroy(() => this.tearDownConnection());
29246
+ }
29247
+ connectTabsWithRouter(component) {
29248
+ if (component.config?.connectWithRouter !== true || !this._hub.isAvailable())
29249
+ return of(false);
29250
+ if (this._connectedComponent === component)
29251
+ return this._activatedRouteChecked$;
29252
+ if (!this._hub.claim(OWNED_PARAMS, this, this._destroyRef, FEATURE_LABEL))
29253
+ return of(false);
29254
+ this._connectedComponent = component;
29255
+ this._hasInitialUrlBeenSet = false;
29256
+ this._activatedRouteCheckedSubject = new ReplaySubject(1);
29257
+ this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
29258
+ this.subscribeToTabChanges();
29259
+ this.subscribeToUrlChanges();
29260
+ return this._activatedRouteChecked$;
29261
+ }
29262
+ tearDownConnection() {
29263
+ this._connectedComponent = undefined;
29264
+ this._hasInitialUrlBeenSet = false;
29265
+ this._readSubscription?.unsubscribe();
29266
+ this._writeSubscription?.unsubscribe();
29267
+ }
29268
+ subscribeToUrlChanges() {
29269
+ let isFirstEmit = true;
29270
+ this._readSubscription = this._hub.navigationSettled$
29271
+ .pipe(switchMap(() => this._hub.queryParams()), map(queryParams => queryParams[TAB_PARAM_NAME]), distinctUntilChanged(), takeUntilDestroyed(this._destroyRef))
29272
+ .subscribe(rawTabName => {
29273
+ this.applyTabFromUrl(rawTabName);
29274
+ if (isFirstEmit) {
29275
+ this._activatedRouteCheckedSubject.next(true);
29276
+ isFirstEmit = false;
29277
+ }
29278
+ });
29279
+ }
29280
+ subscribeToTabChanges() {
29281
+ const component = this._connectedComponent;
29282
+ this._writeSubscription = component.selectionChange
29283
+ .pipe(map(event => event.selectedStep?.config?.name), takeUntilDestroyed(this._destroyRef))
29284
+ .subscribe(name => this.writeUrl(name));
29285
+ }
29286
+ writeUrl(name) {
29287
+ if (!name) {
29288
+ console.warn('Quadrel Framework | QdPageTabs - "connectWithRouter" is active, however the selected <qd-page-tab> has no "name" attribute.');
29289
+ return;
29290
+ }
29291
+ const replaceUrl = !this._hasInitialUrlBeenSet;
29292
+ this._hasInitialUrlBeenSet = true;
29293
+ if (this._hub.snapshotQueryParam(TAB_PARAM_NAME) === name)
29294
+ return;
29295
+ this._hub.write({ [TAB_PARAM_NAME]: name }, { replaceUrl });
29296
+ }
29297
+ applyTabFromUrl(rawTabName) {
29298
+ const component = this._connectedComponent;
29299
+ if (!component)
29300
+ return;
29301
+ if (typeof rawTabName !== 'string' || rawTabName.length === 0) {
29302
+ this.selectFallbackTab();
29303
+ return;
29304
+ }
29305
+ const matchingTab = this.findTabByName(rawTabName);
29306
+ if (matchingTab && !matchingTab.config?.isDisabled) {
29307
+ matchingTab.select();
29308
+ return;
29309
+ }
29310
+ if (matchingTab) {
29311
+ console.warn('Quadrel Framework | QdPageTabs - Tab "' + rawTabName + '" is disabled and cannot be deep-linked.');
29312
+ }
29313
+ else {
29314
+ console.warn('Quadrel Framework | QdPageTabs - No tab found with name "' + rawTabName + '".');
29315
+ }
29316
+ this.selectFallbackTab();
29317
+ }
29318
+ selectFallbackTab() {
29319
+ const component = this._connectedComponent;
29320
+ if (!component)
29321
+ return;
29322
+ const fallbackIndex = this.findFallbackTabIndex();
29323
+ if (fallbackIndex === undefined)
29324
+ return;
29325
+ const fallback = component.tabs.get(fallbackIndex);
29326
+ if (!fallback)
29327
+ return;
29328
+ fallback.select();
29329
+ if (component.config)
29330
+ component.config.selectedIndex = fallbackIndex;
29331
+ this.writeUrl(fallback.config?.name);
29332
+ }
29333
+ findTabByName(name) {
29334
+ return this._connectedComponent?.tabs.find(tab => tab.config?.name === name);
29335
+ }
29336
+ findFallbackTabIndex() {
29337
+ const component = this._connectedComponent;
29338
+ if (!component)
29339
+ return undefined;
29340
+ const configuredIndex = component.config?.selectedIndex;
29341
+ if (typeof configuredIndex === 'number') {
29342
+ const candidate = component.tabs.get(configuredIndex);
29343
+ if (candidate && !candidate.config.isDisabled)
29344
+ return configuredIndex;
29345
+ }
29346
+ const tabsArray = component.tabs.toArray();
29347
+ const firstNonDisabledIndex = tabsArray.findIndex(tab => !tab.config.isDisabled);
29348
+ return firstNonDisabledIndex >= 0 ? firstNonDisabledIndex : undefined;
29349
+ }
29350
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
29351
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsRouterConnectorService });
29352
+ }
29353
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsRouterConnectorService, decorators: [{
29354
+ type: Injectable
29355
+ }], ctorParameters: () => [] });
29356
+
28579
29357
  /**
28580
29358
  * **QdPageTabsComponent** provides a non-linear tabbed navigation system within a **QdPage**.
28581
29359
  * It enables switching between different sections while maintaining form validation and controlled navigation.
@@ -28613,13 +29391,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
28613
29391
  *
28614
29392
  * #### **Submit Button Configuration**
28615
29393
  *
28616
- * The submit button at the bottom of the tab system can be configured via `QdPageTabsConfig`:
29394
+ * The submit button at the bottom of the tab system can be configured via `QdPageTabsConfig.submitButton`:
28617
29395
  *
28618
29396
  * - **i18n**: The translated label for the submit button.
28619
29397
  * - **handler**: A callback function that receives form data upon submission.
28620
29398
  * - **isDisabled**: Controls whether the button should be active or not.
28621
29399
  * - **isHidden**: Determines whether the button is visible.
28622
- * - **connectWithRouter**: If set to true, the tab will be connected to the URL and will be bookmarked. It is obligatory to set the `name` attribute for each tab.
29400
+ *
29401
+ * #### **Router Integration**
29402
+ *
29403
+ * - **connectWithRouter**: If set to true, the active tab is synced with the URL via the `?tab=<name>` query param and becomes bookmarkable. Each `<qd-page-tab>` then needs a unique `name`.
28623
29404
  *
28624
29405
  * #### **Usage**
28625
29406
  *
@@ -28738,8 +29519,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
28738
29519
  class QdPageTabsComponent extends CdkStepper {
28739
29520
  footerService = inject(QdPageFooterService, { optional: true });
28740
29521
  pageStoreService = inject(QdPageStoreService);
28741
- router = inject(Router);
28742
- route = inject(ActivatedRoute, { optional: true });
29522
+ routerConnector = inject(QdPageTabsRouterConnectorService);
28743
29523
  /**
28744
29524
  * Configuration of QdPageTabs.
28745
29525
  */
@@ -28798,8 +29578,11 @@ class QdPageTabsComponent extends CdkStepper {
28798
29578
  ngAfterViewInit() {
28799
29579
  super.ngAfterViewInit();
28800
29580
  setTimeout(() => {
29581
+ if (this.config?.connectWithRouter) {
29582
+ this.configureBookmarkableTabs();
29583
+ return;
29584
+ }
28801
29585
  this.initializeTabSelection();
28802
- this.configureBookmarkableTabs();
28803
29586
  });
28804
29587
  }
28805
29588
  initializeTabSelection() {
@@ -28810,54 +29593,7 @@ class QdPageTabsComponent extends CdkStepper {
28810
29593
  this.selectFirstNotDisabledTab();
28811
29594
  }
28812
29595
  configureBookmarkableTabs() {
28813
- if (this.config?.connectWithRouter) {
28814
- this.initializeTabFromUrl();
28815
- this.initializeFirstTabSelection();
28816
- }
28817
- }
28818
- /**
28819
- * Initializes the tab selection based on the URL parameter 'tab'.
28820
- * @private
28821
- */
28822
- initializeTabFromUrl() {
28823
- this.route.queryParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(params => {
28824
- const tabNameFromParams = params['tab'];
28825
- const pageTab = this.tabs.find(tab => tab.config?.name === tabNameFromParams);
28826
- if (pageTab) {
28827
- pageTab.select();
28828
- }
28829
- else {
28830
- console.warn('Quadrel Framework | QdPageTabs - No tab found with name "' + tabNameFromParams + '".');
28831
- this.selectFirstNotDisabledTab(true);
28832
- }
28833
- });
28834
- }
28835
- /**
28836
- * If the user navigates to a page with a tab selected, the tab will be selected automatically in {@link initializeTabFromUrl()} method.
28837
- * If the user navigates to a page without a tab selected, the first active tab will be selected.
28838
- * @private
28839
- */
28840
- initializeFirstTabSelection() {
28841
- this.route.queryParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(params => {
28842
- const tabNameFromParams = params['tab'];
28843
- if (!tabNameFromParams) {
28844
- const selectedIndex = this.config.selectedIndex;
28845
- const pageTab = this.tabs.find((tab, tabIndex) => tabIndex === selectedIndex);
28846
- if (pageTab && pageTab?.config.name) {
28847
- this.router.navigate([], {
28848
- relativeTo: this.route,
28849
- queryParams: { tab: pageTab.config.name },
28850
- queryParamsHandling: 'merge'
28851
- });
28852
- }
28853
- else {
28854
- console.warn('Quadrel Framework | QdPageTabs - "connectedWithRouter" is active, however <qd-page-tab> has no "name" attribute.');
28855
- }
28856
- }
28857
- });
28858
- }
28859
- ngOnDestroy() {
28860
- super.ngOnDestroy();
29596
+ this.routerConnector.connectTabsWithRouter(this).pipe(takeUntilDestroyed(this.destroyRef)).subscribe();
28861
29597
  }
28862
29598
  isTabSelectable(index) {
28863
29599
  const tab = this.tabs.get(index);
@@ -28865,22 +29601,14 @@ class QdPageTabsComponent extends CdkStepper {
28865
29601
  }
28866
29602
  /**
28867
29603
  * Selects the first tab that is not disabled.
28868
- * @param updateUrl if true, the URL will be updated to reflect the selected tab.
28869
29604
  */
28870
- selectFirstNotDisabledTab(updateUrl = false) {
29605
+ selectFirstNotDisabledTab() {
28871
29606
  this.tabs.some((tab, tabIndex) => {
28872
29607
  if (!tab.config.isDisabled) {
28873
29608
  if (this.config) {
28874
29609
  this.config.selectedIndex = tabIndex;
28875
29610
  }
28876
29611
  this.selectedIndex = tabIndex;
28877
- if (updateUrl && this.config?.connectWithRouter && tab.config?.name) {
28878
- this.router.navigate([], {
28879
- relativeTo: this.route,
28880
- queryParams: { tab: tab.config.name },
28881
- queryParamsHandling: 'merge'
28882
- });
28883
- }
28884
29612
  return true;
28885
29613
  }
28886
29614
  else {
@@ -28897,13 +29625,6 @@ class QdPageTabsComponent extends CdkStepper {
28897
29625
  if (tab.config?.isDisabled)
28898
29626
  return;
28899
29627
  tab.select();
28900
- if (this.config?.connectWithRouter && tab.config?.name) {
28901
- this.router.navigate([], {
28902
- relativeTo: this.route,
28903
- queryParams: { tab: tab.config.name },
28904
- queryParamsHandling: 'merge'
28905
- });
28906
- }
28907
29628
  }
28908
29629
  isSubmitButtonShown() {
28909
29630
  return this.config?.submitButton?.isHidden !== true && !this.footerService;
@@ -28928,11 +29649,11 @@ class QdPageTabsComponent extends CdkStepper {
28928
29649
  });
28929
29650
  }
28930
29651
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
28931
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdPageTabsComponent, isStandalone: true, selector: "qd-page-tabs", inputs: { config: "config", testId: ["data-test-id", "testId"] }, outputs: { tabSelection: "tabSelection" }, host: { properties: { "class.standalone": "!footerService" }, classAttribute: "qd-tabs" }, providers: [{ provide: CdkStepper, useExisting: QdPageTabsComponent }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"qd-tabs-items-container\" [ngClass]=\"{ scrollable: config?.scrollabe }\">\n <qd-page-tab-header\n *ngFor=\"let tab of tabs; let i = index\"\n (click)=\"selectTab(tab)\"\n [id]=\"_getTabLabelId(i)\"\n [index]=\"i\"\n [state]=\"_getIndicatorType(i, tab.state)\"\n [label]=\"tab.config.label.i18n | translate\"\n [counters]=\"tab.config.counters\"\n [isSelected]=\"selectedIndex === i\"\n [isDisabled]=\"tab.config?.isDisabled\"\n [data-test-id]=\"testId + '-header' + '-' + i\"\n >\n </qd-page-tab-header>\n</div>\n\n<div class=\"tabs-content\">\n <ng-container *ngIf=\"!selected?.config?.isDisabled\" [ngTemplateOutlet]=\"selected?.content\"></ng-container>\n</div>\n\n<div class=\"qd-tabs-action-area\" *ngIf=\"isSubmitButtonShown()\">\n <button qdButton (click)=\"save()\" [disabled]=\"isSubmitButtonDisabled()\" [data-test-id]=\"testId + '-submit'\">\n {{ config?.submitButton?.i18n || \"i18n.qd.tabs.button.submit\" | translate }}\n </button>\n</div>\n", styles: [":host{display:flex;flex-direction:column}:host.standalone{height:calc(100% - 50px)}:host .qd-tabs-items-container{display:flex;width:100%;flex-direction:row;flex-wrap:wrap;padding:0 1.25rem;border-bottom:.125rem solid rgb(213,213,213);background-color:#fff;gap:0 .9375rem}@media (max-width: 599.98px){:host .qd-tabs-items-container{padding:0 .9375rem}}@media (max-width: 1279.98px){:host .qd-tabs-items-container.scrollable{flex-wrap:nowrap;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none}}:host .tabs-content{flex:1 1 auto}:host .qd-tabs-action-area{display:flex;width:100%;height:50px;justify-content:flex-end}:host .qd-tabs-action-area button{margin-right:1.25rem}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: QdPageTabHeaderComponent, selector: "qd-page-tab-header", inputs: ["state", "label", "counters", "index", "isSelected", "isDisabled", "data-test-id"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: QdButtonModule }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
29652
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdPageTabsComponent, isStandalone: true, selector: "qd-page-tabs", inputs: { config: "config", testId: ["data-test-id", "testId"] }, outputs: { tabSelection: "tabSelection" }, host: { properties: { "class.standalone": "!footerService" }, classAttribute: "qd-tabs" }, providers: [{ provide: CdkStepper, useExisting: QdPageTabsComponent }, QdPageTabsRouterConnectorService], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"qd-tabs-items-container\" [ngClass]=\"{ scrollable: config?.scrollabe }\">\n <qd-page-tab-header\n *ngFor=\"let tab of tabs; let i = index\"\n (click)=\"selectTab(tab)\"\n [id]=\"_getTabLabelId(i)\"\n [index]=\"i\"\n [state]=\"_getIndicatorType(i, tab.state)\"\n [label]=\"tab.config.label.i18n | translate\"\n [counters]=\"tab.config.counters\"\n [isSelected]=\"selectedIndex === i\"\n [isDisabled]=\"tab.config?.isDisabled\"\n [data-test-id]=\"testId + '-header' + '-' + i\"\n >\n </qd-page-tab-header>\n</div>\n\n<div class=\"tabs-content\">\n <ng-container *ngIf=\"!selected?.config?.isDisabled\" [ngTemplateOutlet]=\"selected?.content\"></ng-container>\n</div>\n\n<div class=\"qd-tabs-action-area\" *ngIf=\"isSubmitButtonShown()\">\n <button qdButton (click)=\"save()\" [disabled]=\"isSubmitButtonDisabled()\" [data-test-id]=\"testId + '-submit'\">\n {{ config?.submitButton?.i18n || \"i18n.qd.tabs.button.submit\" | translate }}\n </button>\n</div>\n", styles: [":host{display:flex;flex-direction:column}:host.standalone{height:calc(100% - 50px)}:host .qd-tabs-items-container{display:flex;width:100%;flex-direction:row;flex-wrap:wrap;padding:0 1.25rem;border-bottom:.125rem solid rgb(213,213,213);background-color:#fff;gap:0 .9375rem}@media (max-width: 599.98px){:host .qd-tabs-items-container{padding:0 .9375rem}}@media (max-width: 1279.98px){:host .qd-tabs-items-container.scrollable{flex-wrap:nowrap;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none}}:host .tabs-content{flex:1 1 auto}:host .qd-tabs-action-area{display:flex;width:100%;height:50px;justify-content:flex-end}:host .qd-tabs-action-area button{margin-right:1.25rem}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: QdPageTabHeaderComponent, selector: "qd-page-tab-header", inputs: ["state", "label", "counters", "index", "isSelected", "isDisabled", "data-test-id"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: QdButtonModule }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
28932
29653
  }
28933
29654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsComponent, decorators: [{
28934
29655
  type: Component,
28935
- args: [{ selector: 'qd-page-tabs', providers: [{ provide: CdkStepper, useExisting: QdPageTabsComponent }], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'qd-tabs', '[class.standalone]': '!footerService' }, standalone: true, imports: [NgFor, NgIf, NgTemplateOutlet, QdPageTabHeaderComponent, TranslateModule, QdButtonModule, CommonModule], template: "<div class=\"qd-tabs-items-container\" [ngClass]=\"{ scrollable: config?.scrollabe }\">\n <qd-page-tab-header\n *ngFor=\"let tab of tabs; let i = index\"\n (click)=\"selectTab(tab)\"\n [id]=\"_getTabLabelId(i)\"\n [index]=\"i\"\n [state]=\"_getIndicatorType(i, tab.state)\"\n [label]=\"tab.config.label.i18n | translate\"\n [counters]=\"tab.config.counters\"\n [isSelected]=\"selectedIndex === i\"\n [isDisabled]=\"tab.config?.isDisabled\"\n [data-test-id]=\"testId + '-header' + '-' + i\"\n >\n </qd-page-tab-header>\n</div>\n\n<div class=\"tabs-content\">\n <ng-container *ngIf=\"!selected?.config?.isDisabled\" [ngTemplateOutlet]=\"selected?.content\"></ng-container>\n</div>\n\n<div class=\"qd-tabs-action-area\" *ngIf=\"isSubmitButtonShown()\">\n <button qdButton (click)=\"save()\" [disabled]=\"isSubmitButtonDisabled()\" [data-test-id]=\"testId + '-submit'\">\n {{ config?.submitButton?.i18n || \"i18n.qd.tabs.button.submit\" | translate }}\n </button>\n</div>\n", styles: [":host{display:flex;flex-direction:column}:host.standalone{height:calc(100% - 50px)}:host .qd-tabs-items-container{display:flex;width:100%;flex-direction:row;flex-wrap:wrap;padding:0 1.25rem;border-bottom:.125rem solid rgb(213,213,213);background-color:#fff;gap:0 .9375rem}@media (max-width: 599.98px){:host .qd-tabs-items-container{padding:0 .9375rem}}@media (max-width: 1279.98px){:host .qd-tabs-items-container.scrollable{flex-wrap:nowrap;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none}}:host .tabs-content{flex:1 1 auto}:host .qd-tabs-action-area{display:flex;width:100%;height:50px;justify-content:flex-end}:host .qd-tabs-action-area button{margin-right:1.25rem}\n"] }]
29656
+ args: [{ selector: 'qd-page-tabs', providers: [{ provide: CdkStepper, useExisting: QdPageTabsComponent }, QdPageTabsRouterConnectorService], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'qd-tabs', '[class.standalone]': '!footerService' }, standalone: true, imports: [NgFor, NgIf, NgTemplateOutlet, QdPageTabHeaderComponent, TranslateModule, QdButtonModule, CommonModule], template: "<div class=\"qd-tabs-items-container\" [ngClass]=\"{ scrollable: config?.scrollabe }\">\n <qd-page-tab-header\n *ngFor=\"let tab of tabs; let i = index\"\n (click)=\"selectTab(tab)\"\n [id]=\"_getTabLabelId(i)\"\n [index]=\"i\"\n [state]=\"_getIndicatorType(i, tab.state)\"\n [label]=\"tab.config.label.i18n | translate\"\n [counters]=\"tab.config.counters\"\n [isSelected]=\"selectedIndex === i\"\n [isDisabled]=\"tab.config?.isDisabled\"\n [data-test-id]=\"testId + '-header' + '-' + i\"\n >\n </qd-page-tab-header>\n</div>\n\n<div class=\"tabs-content\">\n <ng-container *ngIf=\"!selected?.config?.isDisabled\" [ngTemplateOutlet]=\"selected?.content\"></ng-container>\n</div>\n\n<div class=\"qd-tabs-action-area\" *ngIf=\"isSubmitButtonShown()\">\n <button qdButton (click)=\"save()\" [disabled]=\"isSubmitButtonDisabled()\" [data-test-id]=\"testId + '-submit'\">\n {{ config?.submitButton?.i18n || \"i18n.qd.tabs.button.submit\" | translate }}\n </button>\n</div>\n", styles: [":host{display:flex;flex-direction:column}:host.standalone{height:calc(100% - 50px)}:host .qd-tabs-items-container{display:flex;width:100%;flex-direction:row;flex-wrap:wrap;padding:0 1.25rem;border-bottom:.125rem solid rgb(213,213,213);background-color:#fff;gap:0 .9375rem}@media (max-width: 599.98px){:host .qd-tabs-items-container{padding:0 .9375rem}}@media (max-width: 1279.98px){:host .qd-tabs-items-container.scrollable{flex-wrap:nowrap;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none}}:host .tabs-content{flex:1 1 auto}:host .qd-tabs-action-area{display:flex;width:100%;height:50px;justify-content:flex-end}:host .qd-tabs-action-area button{margin-right:1.25rem}\n"] }]
28936
29657
  }], ctorParameters: () => [], propDecorators: { config: [{
28937
29658
  type: Input
28938
29659
  }], testId: [{
@@ -30322,147 +31043,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
30322
31043
  args: [{ selector: 'qd-shell-header-search', standalone: false, template: "<qd-search [configData]=\"config\"> </qd-search>\n", styles: [":host{display:none}:host:not(:last-child){margin-right:1rem}:host ::ng-deep .qd-input-input{border-color:#fff0!important;background:#efefef!important}:host ::ng-deep input{background:#fff0!important}:host ::ng-deep input::placeholder{color:#979797!important}:host ::ng-deep input:hover::placeholder,:host ::ng-deep input:focus::placeholder{color:#171717!important}@media (min-width: 1280px){:host{display:block}}\n"] }]
30323
31044
  }] });
30324
31045
 
30325
- // @ts-strict-ignore
30326
- class QdNavigationService {
30327
- router = inject(Router);
30328
- activatedRoute = inject(ActivatedRoute);
30329
- routeLeafSnapshot$;
30330
- routeData$;
30331
- routeComponentInstanceSubject = new ReplaySubject(1);
30332
- constructor() {
30333
- this.routeLeafSnapshot$ = this.router.events.pipe(filter(event => event instanceof NavigationEnd), map(() => this.activatedRoute), map(route => this.getLeafRoute(route).snapshot), filter(snapshot => snapshot.outlet === 'primary'));
30334
- this.routeData$ = this.routeLeafSnapshot$.pipe(map((snapshot) => snapshot.routeConfig?.data || {}), shareReplay(1));
30335
- }
30336
- getLeafRoute(route) {
30337
- if (!route.firstChild)
30338
- return route;
30339
- return this.getLeafRoute(route.firstChild);
30340
- }
30341
- isHome$() {
30342
- return this.routeData$.pipe(map(({ isHome }) => isHome ?? false));
30343
- }
30344
- getRouteParams$() {
30345
- return this.routeLeafSnapshot$.pipe(map(this.getMergedRouteParams.bind(this)));
30346
- }
30347
- getMergedRouteParams(snapshot) {
30348
- if (!snapshot)
30349
- return {};
30350
- return {
30351
- ...this.getMergedRouteParams(snapshot.parent),
30352
- ...(snapshot.params || {})
30353
- };
30354
- }
30355
- getPreviousHref$() {
30356
- return this.routeData$.pipe(map(({ previousHref }) => previousHref));
30357
- }
30358
- navigate(commands, extras) {
30359
- return this.router.navigate(commands, extras);
30360
- }
30361
- navigateByUrl(url, extras) {
30362
- return this.router.navigateByUrl(url, extras);
30363
- }
30364
- updateRouteComponentInstance(routeComponentInstance) {
30365
- this.routeComponentInstanceSubject.next(routeComponentInstance);
30366
- }
30367
- getRouteComponentInstance$() {
30368
- return this.routeComponentInstanceSubject.asObservable();
30369
- }
30370
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdNavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
30371
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdNavigationService });
30372
- }
30373
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdNavigationService, decorators: [{
30374
- type: Injectable
30375
- }], ctorParameters: () => [] });
30376
-
30377
- // @ts-strict-ignore
30378
- class QdShellLeftService {
30379
- _hasNavigation = new BehaviorSubject(false);
30380
- _isNavigationPinned = new BehaviorSubject(false);
30381
- _isNavigationRolledOver = new BehaviorSubject(false);
30382
- hasNavigation$ = this._hasNavigation.asObservable();
30383
- isNavigationPinned$ = this._isNavigationPinned.asObservable();
30384
- isNavigationRolledOver$ = this._isNavigationRolledOver.asObservable();
30385
- set config(config) {
30386
- this._hasNavigation.next(config.navigation?.length > 0);
30387
- }
30388
- set isNavigationRolledOver(value) {
30389
- this._isNavigationRolledOver.next(value);
30390
- }
30391
- togglePinnedNavigation() {
30392
- this._isNavigationPinned.next(!this._isNavigationPinned.value);
30393
- }
30394
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellLeftService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
30395
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellLeftService });
30396
- }
30397
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellLeftService, decorators: [{
30398
- type: Injectable
30399
- }] });
30400
-
30401
- class QdShellRightService {
30402
- notificationsService = inject(QdNotificationsService);
30403
- _open = new BehaviorSubject(false);
30404
- _pinned = new BehaviorSubject(false);
30405
- _hasNotificationsToggle = new BehaviorSubject(false);
30406
- open$ = this._open.asObservable();
30407
- pinned$ = this._pinned.asObservable();
30408
- notifications$;
30409
- set open(open) {
30410
- this._open.next(open);
30411
- }
30412
- get hasNotificationsToggle$() {
30413
- return combineLatest([this._hasNotificationsToggle, this.hasNotifications$]).pipe(map(([hasNotificationsToggle, hasNotifications]) => hasNotificationsToggle || hasNotifications));
30414
- }
30415
- get hasNotifications$() {
30416
- return this.notifications$.pipe(map(notifications => notifications.length > 0));
30417
- }
30418
- set config(config) {
30419
- this._hasNotificationsToggle.next(config.hasNotificationsToggle ?? false);
30420
- }
30421
- constructor() {
30422
- const notificationsService = this.notificationsService;
30423
- this.notifications$ = notificationsService.getNotificationsForContext('shell');
30424
- this._open.pipe(filter(open => open)).subscribe(() => {
30425
- this._pinned.next(false);
30426
- });
30427
- }
30428
- toggleOpen() {
30429
- this._open.next(!this._open.value);
30430
- }
30431
- togglePinned() {
30432
- this._pinned.next(!this._pinned.value);
30433
- }
30434
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellRightService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
30435
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellRightService });
30436
- }
30437
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellRightService, decorators: [{
30438
- type: Injectable
30439
- }], ctorParameters: () => [] });
30440
-
30441
- // @ts-strict-ignore
30442
- class QdShellHeaderMinimizationService {
30443
- navigationService = inject(QdNavigationService);
30444
- breakpointService = inject(QdBreakpointService);
30445
- _headerMinimizedBreakpoints = ['sm', 'xs'];
30446
- _config;
30447
- _isInternal$ = new BehaviorSubject(false);
30448
- set config(config) {
30449
- this._config = config;
30450
- this._isInternal$.next(config.isInternal);
30451
- }
30452
- isHeaderMinimized$() {
30453
- const isMinimizedForAllBreakpoints$ = this._isInternal$.pipe(combineLatestWith(this.navigationService.isHome$()), map(([isInternal, isHomePage]) => isInternal ?? !isHomePage));
30454
- return isMinimizedForAllBreakpoints$.pipe(combineLatestWith(this.breakpointService.getMatchingBreakpoint()), map(([isMinimizedForAllBreakpoints, breakpoint]) => isMinimizedForAllBreakpoints || this._isMinimizedBreakpoint(breakpoint)));
30455
- }
30456
- _isMinimizedBreakpoint(breakpoint) {
30457
- return this._headerMinimizedBreakpoints.includes(breakpoint);
30458
- }
30459
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellHeaderMinimizationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
30460
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellHeaderMinimizationService });
30461
- }
30462
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellHeaderMinimizationService, decorators: [{
30463
- type: Injectable
30464
- }] });
30465
-
30466
31046
  // @ts-strict-ignore
30467
31047
  const loadJavascriptAsset = (path, scriptElementAttributes = {}) => {
30468
31048
  const scriptElement = document.createElement('script');
@@ -30918,6 +31498,162 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
30918
31498
  args: ['serviceNavigation']
30919
31499
  }] } });
30920
31500
 
31501
+ class QdShellServiceNavigationModule {
31502
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellServiceNavigationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
31503
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: QdShellServiceNavigationModule, declarations: [QdShellServiceNavigationComponent], imports: [CommonModule], exports: [QdShellServiceNavigationComponent] });
31504
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellServiceNavigationModule, imports: [CommonModule] });
31505
+ }
31506
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellServiceNavigationModule, decorators: [{
31507
+ type: NgModule,
31508
+ args: [{
31509
+ imports: [CommonModule],
31510
+ declarations: [QdShellServiceNavigationComponent],
31511
+ exports: [QdShellServiceNavigationComponent],
31512
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
31513
+ }]
31514
+ }] });
31515
+
31516
+ // @ts-strict-ignore
31517
+ class QdNavigationService {
31518
+ router = inject(Router);
31519
+ activatedRoute = inject(ActivatedRoute);
31520
+ routeLeafSnapshot$;
31521
+ routeData$;
31522
+ routeComponentInstanceSubject = new ReplaySubject(1);
31523
+ constructor() {
31524
+ this.routeLeafSnapshot$ = this.router.events.pipe(filter(event => event instanceof NavigationEnd), map(() => this.activatedRoute), map(route => this.getLeafRoute(route).snapshot), filter(snapshot => snapshot.outlet === 'primary'));
31525
+ this.routeData$ = this.routeLeafSnapshot$.pipe(map((snapshot) => snapshot.routeConfig?.data || {}), shareReplay(1));
31526
+ }
31527
+ getLeafRoute(route) {
31528
+ if (!route.firstChild)
31529
+ return route;
31530
+ return this.getLeafRoute(route.firstChild);
31531
+ }
31532
+ isHome$() {
31533
+ return this.routeData$.pipe(map(({ isHome }) => isHome ?? false));
31534
+ }
31535
+ getRouteParams$() {
31536
+ return this.routeLeafSnapshot$.pipe(map(this.getMergedRouteParams.bind(this)));
31537
+ }
31538
+ getMergedRouteParams(snapshot) {
31539
+ if (!snapshot)
31540
+ return {};
31541
+ return {
31542
+ ...this.getMergedRouteParams(snapshot.parent),
31543
+ ...(snapshot.params || {})
31544
+ };
31545
+ }
31546
+ getPreviousHref$() {
31547
+ return this.routeData$.pipe(map(({ previousHref }) => previousHref));
31548
+ }
31549
+ navigate(commands, extras) {
31550
+ return this.router.navigate(commands, extras);
31551
+ }
31552
+ navigateByUrl(url, extras) {
31553
+ return this.router.navigateByUrl(url, extras);
31554
+ }
31555
+ updateRouteComponentInstance(routeComponentInstance) {
31556
+ this.routeComponentInstanceSubject.next(routeComponentInstance);
31557
+ }
31558
+ getRouteComponentInstance$() {
31559
+ return this.routeComponentInstanceSubject.asObservable();
31560
+ }
31561
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdNavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
31562
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdNavigationService });
31563
+ }
31564
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdNavigationService, decorators: [{
31565
+ type: Injectable
31566
+ }], ctorParameters: () => [] });
31567
+
31568
+ // @ts-strict-ignore
31569
+ class QdShellLeftService {
31570
+ _hasNavigation = new BehaviorSubject(false);
31571
+ _isNavigationPinned = new BehaviorSubject(false);
31572
+ _isNavigationRolledOver = new BehaviorSubject(false);
31573
+ hasNavigation$ = this._hasNavigation.asObservable();
31574
+ isNavigationPinned$ = this._isNavigationPinned.asObservable();
31575
+ isNavigationRolledOver$ = this._isNavigationRolledOver.asObservable();
31576
+ set config(config) {
31577
+ this._hasNavigation.next(config.navigation?.length > 0);
31578
+ }
31579
+ set isNavigationRolledOver(value) {
31580
+ this._isNavigationRolledOver.next(value);
31581
+ }
31582
+ togglePinnedNavigation() {
31583
+ this._isNavigationPinned.next(!this._isNavigationPinned.value);
31584
+ }
31585
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellLeftService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
31586
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellLeftService });
31587
+ }
31588
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellLeftService, decorators: [{
31589
+ type: Injectable
31590
+ }] });
31591
+
31592
+ class QdShellRightService {
31593
+ notificationsService = inject(QdNotificationsService);
31594
+ _open = new BehaviorSubject(false);
31595
+ _pinned = new BehaviorSubject(false);
31596
+ _hasNotificationsToggle = new BehaviorSubject(false);
31597
+ open$ = this._open.asObservable();
31598
+ pinned$ = this._pinned.asObservable();
31599
+ notifications$;
31600
+ set open(open) {
31601
+ this._open.next(open);
31602
+ }
31603
+ get hasNotificationsToggle$() {
31604
+ return combineLatest([this._hasNotificationsToggle, this.hasNotifications$]).pipe(map(([hasNotificationsToggle, hasNotifications]) => hasNotificationsToggle || hasNotifications));
31605
+ }
31606
+ get hasNotifications$() {
31607
+ return this.notifications$.pipe(map(notifications => notifications.length > 0));
31608
+ }
31609
+ set config(config) {
31610
+ this._hasNotificationsToggle.next(config.hasNotificationsToggle ?? false);
31611
+ }
31612
+ constructor() {
31613
+ const notificationsService = this.notificationsService;
31614
+ this.notifications$ = notificationsService.getNotificationsForContext('shell');
31615
+ this._open.pipe(filter(open => open)).subscribe(() => {
31616
+ this._pinned.next(false);
31617
+ });
31618
+ }
31619
+ toggleOpen() {
31620
+ this._open.next(!this._open.value);
31621
+ }
31622
+ togglePinned() {
31623
+ this._pinned.next(!this._pinned.value);
31624
+ }
31625
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellRightService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
31626
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellRightService });
31627
+ }
31628
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellRightService, decorators: [{
31629
+ type: Injectable
31630
+ }], ctorParameters: () => [] });
31631
+
31632
+ // @ts-strict-ignore
31633
+ class QdShellHeaderMinimizationService {
31634
+ navigationService = inject(QdNavigationService);
31635
+ breakpointService = inject(QdBreakpointService);
31636
+ _headerMinimizedBreakpoints = ['sm', 'xs'];
31637
+ _config;
31638
+ _isInternal$ = new BehaviorSubject(false);
31639
+ set config(config) {
31640
+ this._config = config;
31641
+ this._isInternal$.next(config.isInternal);
31642
+ }
31643
+ isHeaderMinimized$() {
31644
+ const isMinimizedForAllBreakpoints$ = this._isInternal$.pipe(combineLatestWith(this.navigationService.isHome$()), map(([isInternal, isHomePage]) => isInternal ?? !isHomePage));
31645
+ return isMinimizedForAllBreakpoints$.pipe(combineLatestWith(this.breakpointService.getMatchingBreakpoint()), map(([isMinimizedForAllBreakpoints, breakpoint]) => isMinimizedForAllBreakpoints || this._isMinimizedBreakpoint(breakpoint)));
31646
+ }
31647
+ _isMinimizedBreakpoint(breakpoint) {
31648
+ return this._headerMinimizedBreakpoints.includes(breakpoint);
31649
+ }
31650
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellHeaderMinimizationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
31651
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellHeaderMinimizationService });
31652
+ }
31653
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellHeaderMinimizationService, decorators: [{
31654
+ type: Injectable
31655
+ }] });
31656
+
30921
31657
  // @ts-strict-ignore
30922
31658
  const DEFAULT_LANGUAGE_LIST$1 = ['de', 'fr', 'it', 'en'];
30923
31659
  class QdShellHeaderWidgetService {
@@ -31312,7 +32048,7 @@ class QdShellHeaderComponent {
31312
32048
  const navigationService = this.navigationService;
31313
32049
  const shellLeftService = this.shellLeftService;
31314
32050
  const shellRightService = this.shellRightService;
31315
- this.backLinkDisplayed$ = navigationService.getPreviousHref$().pipe(withLatestFrom(navigationService.getRouteComponentInstance$()), switchMap(([previousHref, routeComponentInstance]) => {
32051
+ this.backLinkDisplayed$ = navigationService.getPreviousHref$().pipe(withLatestFrom$1(navigationService.getRouteComponentInstance$()), switchMap(([previousHref, routeComponentInstance]) => {
31316
32052
  if (typeof previousHref === 'function')
31317
32053
  previousHref = previousHref(routeComponentInstance);
31318
32054
  return isObservable(previousHref) ? previousHref.pipe(take(1)) : of(previousHref);
@@ -32000,21 +32736,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
32000
32736
  args: [QdCommentsComponent]
32001
32737
  }] } });
32002
32738
 
32003
- class QdShellServiceNavigationModule {
32004
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellServiceNavigationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
32005
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: QdShellServiceNavigationModule, declarations: [QdShellServiceNavigationComponent], imports: [CommonModule], exports: [QdShellServiceNavigationComponent] });
32006
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellServiceNavigationModule, imports: [CommonModule] });
32007
- }
32008
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdShellServiceNavigationModule, decorators: [{
32009
- type: NgModule,
32010
- args: [{
32011
- imports: [CommonModule],
32012
- declarations: [QdShellServiceNavigationComponent],
32013
- exports: [QdShellServiceNavigationComponent],
32014
- schemas: [CUSTOM_ELEMENTS_SCHEMA]
32015
- }]
32016
- }] });
32017
-
32018
32739
  function initializeBreadcrumbServices(breadcrumbsService, dialogBreadcrumbsService) {
32019
32740
  return () => {
32020
32741
  breadcrumbsService.initialize();
@@ -33267,5 +33988,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
33267
33988
  * Generated bundle index. Do not edit.
33268
33989
  */
33269
33990
 
33270
- export { APP_ENVIRONMENT, AVAILABLE_ICONS, BACKEND_ERROR_CODES, MockLocaleDatePipe, NavigationTileComponent, NavigationTilesComponent, QD_DIALOG_CONFIRMATION_RESOLVER_TOKEN, QD_FILE_MANAGER_TOKEN, QD_FILE_UPLOAD_MANAGER_TOKEN, QD_FORM_OPTIONS_RESOLVER, QD_PAGE_OBJECT_RESOLVER_TOKEN, QD_PAGE_STEP_RESOLVER_TOKEN, QD_POPOVER_TOP_FIRST, QD_SAFE_BOTTOM_OFFSET, QD_TABLE_DATA_RESOLVER_TOKEN, QD_UPLOAD_HTTP_OPTIONS, QdButtonComponent, QdButtonGhostDirective, QdButtonGridComponent, QdButtonLinkDirective, QdButtonModule, QdButtonStackButtonComponent, QdButtonStackComponent, QdCheckboxChipsComponent, QdCheckboxComponent, QdCheckboxesComponent, QdChipComponent, QdChipModule, QdColumnAutoFillDirective, QdColumnBreakBeforeDirective, QdColumnDirective, QdColumnDisableResponsiveColspansDirective, QdColumnFullGridWidthDirective, QdColumnNextInSameRowDirective, QdColumnsDirective, QdColumnsDisableAutoFillDirective, QdColumnsDisableResponsiveColspansDirective, QdColumnsMaxDirective, QdCommentsComponent, QdCommentsModule, QdConnectFormStateToPageDirective, QdConnectorTableContextDirective, QdConnectorTableFilterDirective, QdConnectorTableSearchDirective, QdContactCardComponent, QdContactCardModule, QdContainerPairsCaptionComponent, QdContainerPairsContainerComponent, QdContainerPairsHeaderComponent, QdContainerPairsItemComponent, QdContainerPairsValueComponent, QdContextService, QdCoreModule, QdDatepickerComponent, QdDialogActionComponent, QdDialogAuthSessionEndComponent, QdDialogAuthSessionEndService, QdDialogComponent, QdDialogConfirmationComponent, QdDialogConfirmationErrorDirective, QdDialogConfirmationInfoDirective, QdDialogConfirmationSuccessDirective, QdDialogModule, QdDialogRecordStepperComponent, QdDialogService, QdDialogSize, QdDisabledDirective, QdDropdownComponent, QdFileCollectorComponent, QdFileCollectorModule, QdFileSizePipe$1 as QdFileSizePipe, QdFileUploadComponent, QdFileUploadService, QdFilterComponent, QdFilterFormItemsComponent, QdFilterModule, QdFilterRestParamBuilder, QdFilterService, QdFormArray, QdFormBuilder, QdFormControl, QdFormGroup, QdFormModule, QdGridComponent, QdGridModule, QdHorizontalPairsCaptionComponent, QdHorizontalPairsComponent, QdHorizontalPairsItemComponent, QdHorizontalPairsValueComponent, QdIconButtonComponent, QdIconComponent, QdIconModule, QdImageComponent, QdImageModule, QdIndeterminateProgressBarComponent, QdInputComponent, QdListModule, QdMenuButtonComponent, QdMockBreakpointService, QdMockButtonComponent, QdMockButtonGhostDirective, QdMockButtonGridComponent, QdMockButtonLinkDirective, QdMockButtonModule, QdMockButtonStackButtonComponent, QdMockButtonStackComponent, QdMockCalendarComponent, QdMockCheckboxChipsComponent, QdMockCheckboxComponent, QdMockCheckboxesComponent, QdMockChipComponent, QdMockChipModule, QdMockColumnDirective, QdMockColumnsDirective, QdMockContactCardComponent, QdMockContactCardModule, QdMockContainerPairsCaptionComponent, QdMockContainerPairsContainerComponent, QdMockContainerPairsHeaderComponent, QdMockContainerPairsItemComponent, QdMockContainerPairsValueComponent, QdMockCoreModule, QdMockCounterBadgeComponent, QdMockDatepickerComponent, QdMockDisabledDirective, QdMockDropdownComponent, QdMockFileCollectorComponent, QdMockFileCollectorModule, QdMockFilterCategoryBooleanComponent, QdMockFilterCategoryComponent, QdMockFilterCategoryDateComponent, QdMockFilterCategoryDateRangeComponent, QdMockFilterCategoryFreeTextComponent, QdMockFilterCategorySelectComponent, QdMockFilterComponent, QdMockFilterFormItemsComponent, QdMockFilterItemBooleanComponent, QdMockFilterItemDateComponent, QdMockFilterItemDateRangeComponent, QdMockFilterItemFreeTextComponent, QdMockFilterItemMultiSelectComponent, QdMockFilterItemSingleSelectComponent, QdMockFilterModule, QdMockFilterService, QdMockFormErrorComponent, QdMockFormGroupErrorComponent, QdMockFormHintComponent, QdMockFormLabelComponent, QdMockFormReadonlyComponent, QdMockFormViewonlyComponent, QdMockFormsModule, QdMockGridModule, QdMockIconButtonComponent, QdMockIconComponent, QdMockIconModule, QdMockImageComponent, QdMockImageModule, QdMockIndeterminateProgressBarComponent, QdMockInputComponent, QdMockListModule, QdMockNavigationTileComponent, QdMockNavigationTilesComponent, QdMockNavigationTilesModule, QdMockNotificationComponent, QdMockNotificationContentComponent, QdMockNotificationsComponent, QdMockNotificationsModule, QdMockNotificationsService, QdMockPageComponent, QdMockPageModule, QdMockPercentageProgressBarComponent, QdMockPinCodeComponent, QdMockPlaceHolderModule, QdMockPopoverOnClickDirective, QdMockProgressBarModule, QdMockQdPlaceHolderComponent, QdMockRadioButtonsComponent, QdMockRwdDisabledDirective, QdMockSearchComponent, QdMockSearchModule, QdMockSectionComponent, QdMockSectionModule, QdMockShellComponent, QdMockShellFooterComponent, QdMockShellHeaderBannerComponent, QdMockShellHeaderComponent, QdMockShellHeaderSearchComponent, QdMockShellHeaderWidgetComponent, QdMockShellModule, QdMockShellToolbarComponent, QdMockShellToolbarItemComponent, QdMockStatusIndicatorCaptionComponent, QdMockStatusIndicatorComponent, QdMockStatusIndicatorItemComponent, QdMockStatusIndicatorModule, QdMockStatusPairsCaptionComponent, QdMockStatusPairsComponent, QdMockStatusPairsErrorComponent, QdMockStatusPairsItemComponent, QdMockStatusPairsValueComponent, QdMockSwitchComponent, QdMockSwitchesComponent, QdMockTableComponent, QdMockTableModule, QdMockTextSectionComponent, QdMockTextSectionHeadlineComponent, QdMockTextSectionModule, QdMockTextSectionParagraphComponent, QdMockTextareaComponent, QdMockTileButtonListComponent, QdMockTileComponent, QdMockTileTextListComponent, QdMockTileTextListItemComponent, QdMockTileTitleComponent, QdMockTilesContainerComponent, QdMockTilesContainerTitleComponent, QdMockTilesModule, QdMockTranslatePipe, QdMockVisuallyHiddenDirective, QdMultiInputComponent, QdNavigationTilesModule, QdNotificationComponent, QdNotificationContentComponent, QdNotificationsComponent, QdNotificationsHttpInterceptorService, QdNotificationsModule, QdNotificationsService, QdNotificationsSnackbarListenerDirective, QdPageComponent, QdPageControlPanelComponent, QdPageFooterComponent, QdPageFooterCustomContentDirective, QdPageInfoBannerComponent, QdPageModule, QdPageStepComponent, QdPageStepperAdapterDirective, QdPageStepperComponent, QdPageStepperModule, QdPageStoreService, QdPageTabComponent, QdPageTabsAdapterDirective, QdPageTabsComponent, QdPageTabsModule, QdPanelSectionActionsComponent, QdPanelSectionComponent, QdPanelSectionModule, QdPanelSectionStatusComponent, QdPanelSectionTextParagraphComponent, QdPendingChangesGuardDirective, QdPercentageProgressBarComponent, QdPinCodeComponent, QdPlaceHolderComponent, QdPlaceHolderModule, QdPlaceholderPipe, QdProgressBarModule, QdProjectionGuardComponent, QdPushEventsService, QdQuickEditComponent, QdQuickEditModule, QdRadioButtonsComponent, QdRichtextComponent, QdRwdDisabledDirective, QdSearchComponent, QdSearchModule, QdSectionAdapterDirective, QdSectionComponent, QdSectionModule, QdSectionToolbarComponent, QdShellComponent, QdShellModule, QdSortDirection, QdSpinnerComponent, QdSpinnerModule, QdStatusIndicatorComponent, QdStatusIndicatorModule, QdStatusPairsCaptionComponent, QdStatusPairsComponent, QdStatusPairsErrorComponent, QdStatusPairsItemComponent, QdStatusPairsValueComponent, QdSubgridComponent, QdSwitchComponent, QdSwitchesComponent, QdTableComponent, QdTableModule, QdTableSpringTools, QdTextSectionComponent, QdTextSectionHeadlineComponent, QdTextSectionModule, QdTextSectionParagraphComponent, QdTextareaComponent, QdTileButtonListComponent, QdTileComponent, QdTileTextListComponent, QdTileTextListItemComponent, QdTileTitleComponent, QdTilesComponent, QdTilesModule, QdTilesTitleComponent, QdTooltipAtIntersectionDirective, QdTooltipIconComponent, QdTreeComponent, QdTreeModule, QdTreeRowExpanderService, QdUiMockModule, QdUiModule, QdUploadErrorType, QdValidators, QdViewportAdaptiveDirective, QdVisuallyHiddenDirective, chipColorDefault, createMetadataStream, updateHtmlLang };
33991
+ export { APP_ENVIRONMENT, AVAILABLE_ICONS, BACKEND_ERROR_CODES, MockLocaleDatePipe, NavigationTileComponent, NavigationTilesComponent, QD_DIALOG_CONFIRMATION_RESOLVER_TOKEN, QD_FILE_MANAGER_TOKEN, QD_FILE_UPLOAD_MANAGER_TOKEN, QD_FORM_OPTIONS_RESOLVER, QD_PAGE_OBJECT_RESOLVER_TOKEN, QD_PAGE_STEP_RESOLVER_TOKEN, QD_POPOVER_TOP_FIRST, QD_SAFE_BOTTOM_OFFSET, QD_TABLE_DATA_RESOLVER_TOKEN, QD_UPLOAD_HTTP_OPTIONS, QdButtonComponent, QdButtonGhostDirective, QdButtonGridComponent, QdButtonLinkDirective, QdButtonModule, QdButtonStackButtonComponent, QdButtonStackComponent, QdCheckboxChipsComponent, QdCheckboxComponent, QdCheckboxesComponent, QdChipComponent, QdChipModule, QdColumnAutoFillDirective, QdColumnBreakBeforeDirective, QdColumnDirective, QdColumnDisableResponsiveColspansDirective, QdColumnFullGridWidthDirective, QdColumnNextInSameRowDirective, QdColumnsDirective, QdColumnsDisableAutoFillDirective, QdColumnsDisableResponsiveColspansDirective, QdColumnsMaxDirective, QdCommentsComponent, QdCommentsModule, QdConnectFormStateToPageDirective, QdConnectorTableContextDirective, QdConnectorTableFilterDirective, QdConnectorTableSearchDirective, QdContactCardComponent, QdContactCardModule, QdContainerPairsCaptionComponent, QdContainerPairsContainerComponent, QdContainerPairsHeaderComponent, QdContainerPairsItemComponent, QdContainerPairsValueComponent, QdContextService, QdCoreModule, QdDatepickerComponent, QdDialogActionComponent, QdDialogAuthSessionEndComponent, QdDialogAuthSessionEndService, QdDialogComponent, QdDialogConfirmationComponent, QdDialogConfirmationErrorDirective, QdDialogConfirmationInfoDirective, QdDialogConfirmationSuccessDirective, QdDialogModule, QdDialogRecordStepperComponent, QdDialogService, QdDialogSize, QdDisabledDirective, QdDropdownComponent, QdFileCollectorComponent, QdFileCollectorModule, QdFileSizePipe$1 as QdFileSizePipe, QdFileUploadComponent, QdFileUploadService, QdFilterComponent, QdFilterFormItemsComponent, QdFilterModule, QdFilterRestParamBuilder, QdFilterService, QdFormArray, QdFormBuilder, QdFormControl, QdFormGroup, QdFormModule, QdGridComponent, QdGridModule, QdHorizontalPairsCaptionComponent, QdHorizontalPairsComponent, QdHorizontalPairsItemComponent, QdHorizontalPairsValueComponent, QdIconButtonComponent, QdIconComponent, QdIconModule, QdImageComponent, QdImageModule, QdIndeterminateProgressBarComponent, QdInputComponent, QdListModule, QdMenuButtonComponent, QdMockBreakpointService, QdMockButtonComponent, QdMockButtonGhostDirective, QdMockButtonGridComponent, QdMockButtonLinkDirective, QdMockButtonModule, QdMockButtonStackButtonComponent, QdMockButtonStackComponent, QdMockCalendarComponent, QdMockCheckboxChipsComponent, QdMockCheckboxComponent, QdMockCheckboxesComponent, QdMockChipComponent, QdMockChipModule, QdMockColumnDirective, QdMockColumnsDirective, QdMockContactCardComponent, QdMockContactCardModule, QdMockContainerPairsCaptionComponent, QdMockContainerPairsContainerComponent, QdMockContainerPairsHeaderComponent, QdMockContainerPairsItemComponent, QdMockContainerPairsValueComponent, QdMockCoreModule, QdMockCounterBadgeComponent, QdMockDatepickerComponent, QdMockDisabledDirective, QdMockDropdownComponent, QdMockFileCollectorComponent, QdMockFileCollectorModule, QdMockFilterCategoryBooleanComponent, QdMockFilterCategoryComponent, QdMockFilterCategoryDateComponent, QdMockFilterCategoryDateRangeComponent, QdMockFilterCategoryFreeTextComponent, QdMockFilterCategorySelectComponent, QdMockFilterComponent, QdMockFilterFormItemsComponent, QdMockFilterItemBooleanComponent, QdMockFilterItemDateComponent, QdMockFilterItemDateRangeComponent, QdMockFilterItemFreeTextComponent, QdMockFilterItemMultiSelectComponent, QdMockFilterItemSingleSelectComponent, QdMockFilterModule, QdMockFilterService, QdMockFormErrorComponent, QdMockFormGroupErrorComponent, QdMockFormHintComponent, QdMockFormLabelComponent, QdMockFormReadonlyComponent, QdMockFormViewonlyComponent, QdMockFormsModule, QdMockGridModule, QdMockIconButtonComponent, QdMockIconComponent, QdMockIconModule, QdMockImageComponent, QdMockImageModule, QdMockIndeterminateProgressBarComponent, QdMockInputComponent, QdMockListModule, QdMockNavigationTileComponent, QdMockNavigationTilesComponent, QdMockNavigationTilesModule, QdMockNotificationComponent, QdMockNotificationContentComponent, QdMockNotificationsComponent, QdMockNotificationsModule, QdMockNotificationsService, QdMockPageComponent, QdMockPageModule, QdMockPercentageProgressBarComponent, QdMockPinCodeComponent, QdMockPlaceHolderModule, QdMockPopoverOnClickDirective, QdMockProgressBarModule, QdMockQdPlaceHolderComponent, QdMockRadioButtonsComponent, QdMockRwdDisabledDirective, QdMockSearchComponent, QdMockSearchModule, QdMockSectionComponent, QdMockSectionModule, QdMockShellComponent, QdMockShellFooterComponent, QdMockShellHeaderBannerComponent, QdMockShellHeaderComponent, QdMockShellHeaderSearchComponent, QdMockShellHeaderWidgetComponent, QdMockShellModule, QdMockShellToolbarComponent, QdMockShellToolbarItemComponent, QdMockStatusIndicatorCaptionComponent, QdMockStatusIndicatorComponent, QdMockStatusIndicatorItemComponent, QdMockStatusIndicatorModule, QdMockStatusPairsCaptionComponent, QdMockStatusPairsComponent, QdMockStatusPairsErrorComponent, QdMockStatusPairsItemComponent, QdMockStatusPairsValueComponent, QdMockSwitchComponent, QdMockSwitchesComponent, QdMockTableComponent, QdMockTableModule, QdMockTextSectionComponent, QdMockTextSectionHeadlineComponent, QdMockTextSectionModule, QdMockTextSectionParagraphComponent, QdMockTextareaComponent, QdMockTileButtonListComponent, QdMockTileComponent, QdMockTileTextListComponent, QdMockTileTextListItemComponent, QdMockTileTitleComponent, QdMockTilesContainerComponent, QdMockTilesContainerTitleComponent, QdMockTilesModule, QdMockTranslatePipe, QdMockVisuallyHiddenDirective, QdMultiInputComponent, QdNavigationTilesModule, QdNotificationComponent, QdNotificationContentComponent, QdNotificationsComponent, QdNotificationsHttpInterceptorService, QdNotificationsModule, QdNotificationsService, QdNotificationsSnackbarListenerDirective, QdPageComponent, QdPageControlPanelComponent, QdPageFooterComponent, QdPageFooterCustomContentDirective, QdPageInfoBannerComponent, QdPageModule, QdPageStepComponent, QdPageStepperAdapterDirective, QdPageStepperComponent, QdPageStepperModule, QdPageStoreService, QdPageTabComponent, QdPageTabsAdapterDirective, QdPageTabsComponent, QdPageTabsModule, QdPanelSectionActionsComponent, QdPanelSectionComponent, QdPanelSectionModule, QdPanelSectionStatusComponent, QdPanelSectionTextParagraphComponent, QdPendingChangesGuardDirective, QdPercentageProgressBarComponent, QdPinCodeComponent, QdPlaceHolderComponent, QdPlaceHolderModule, QdPlaceholderPipe, QdProgressBarModule, QdProjectionGuardComponent, QdPushEventsService, QdQuickEditComponent, QdQuickEditModule, QdRadioButtonsComponent, QdRichtextComponent, QdRouterQueryParamHubService, QdRwdDisabledDirective, QdSearchComponent, QdSearchModule, QdSectionAdapterDirective, QdSectionComponent, QdSectionModule, QdSectionToolbarComponent, QdShellComponent, QdShellModule, QdSortDirection, QdSpinnerComponent, QdSpinnerModule, QdStatusIndicatorComponent, QdStatusIndicatorModule, QdStatusPairsCaptionComponent, QdStatusPairsComponent, QdStatusPairsErrorComponent, QdStatusPairsItemComponent, QdStatusPairsValueComponent, QdSubgridComponent, QdSwitchComponent, QdSwitchesComponent, QdTableComponent, QdTableModule, QdTableSpringTools, QdTextSectionComponent, QdTextSectionHeadlineComponent, QdTextSectionModule, QdTextSectionParagraphComponent, QdTextareaComponent, QdTileButtonListComponent, QdTileComponent, QdTileTextListComponent, QdTileTextListItemComponent, QdTileTitleComponent, QdTilesComponent, QdTilesModule, QdTilesTitleComponent, QdTooltipAtIntersectionDirective, QdTooltipIconComponent, QdTreeComponent, QdTreeModule, QdTreeRowExpanderService, QdUiMockModule, QdUiModule, QdUploadErrorType, QdValidators, QdViewportAdaptiveDirective, QdVisuallyHiddenDirective, chipColorDefault, createMetadataStream, updateHtmlLang };
33271
33992
  //# sourceMappingURL=quadrel-enterprise-ui-framework.mjs.map