@quadrel-enterprise-ui/framework 20.12.0 → 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.
|
@@ -3,8 +3,8 @@ import { inject, ElementRef, Directive, InjectionToken, HostBinding, Input, View
|
|
|
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, forkJoin, delayWhen, combineLatestWith, 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';
|
|
@@ -7118,9 +7118,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
7118
7118
|
/**
|
|
7119
7119
|
* Framework-wide funnel for syncing application state with the URL `?key=value` query string.
|
|
7120
7120
|
*
|
|
7121
|
-
* Several Quadrel features mirror their state to the URL
|
|
7122
|
-
*
|
|
7123
|
-
* `router.navigate(...)` directly, which produces two failure modes:
|
|
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:
|
|
7124
7123
|
*
|
|
7125
7124
|
* 1. **Race**: two navigates issued in the same JS turn cancel each other. Only the last writer
|
|
7126
7125
|
* wins; the URL silently loses the cancelled feature's params.
|
|
@@ -7150,7 +7149,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
7150
7149
|
*
|
|
7151
7150
|
* connect(state$: Observable<FilterState>, dispatch: (state: FilterState) => void): void {
|
|
7152
7151
|
* if (!this._hub.isAvailable()) return;
|
|
7153
|
-
* if (!this._hub.claim(['filter'], this, this._destroyRef)) return;
|
|
7152
|
+
* if (!this._hub.claim(['filter'], this, this._destroyRef, { name: 'Filter', plural: 'filters' })) return;
|
|
7154
7153
|
*
|
|
7155
7154
|
* this._hub
|
|
7156
7155
|
* .queryParams()
|
|
@@ -7159,7 +7158,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
7159
7158
|
*
|
|
7160
7159
|
* state$
|
|
7161
7160
|
* .pipe(takeUntilDestroyed(this._destroyRef))
|
|
7162
|
-
* .subscribe(state => this._hub.write({ filter: serializeFilter(state) }, false));
|
|
7161
|
+
* .subscribe(state => this._hub.write({ filter: serializeFilter(state) }, { replaceUrl: false }));
|
|
7163
7162
|
* }
|
|
7164
7163
|
* }
|
|
7165
7164
|
* ```
|
|
@@ -7187,6 +7186,7 @@ class QdRouterQueryParamHubService {
|
|
|
7187
7186
|
_activatedRoute = inject(ActivatedRoute, { optional: true });
|
|
7188
7187
|
_destroyRef = inject(DestroyRef);
|
|
7189
7188
|
_ownership = new Map();
|
|
7189
|
+
_ownerLabels = new WeakMap();
|
|
7190
7190
|
_destroyClaims = new WeakMap();
|
|
7191
7191
|
_navigationSettledSubject = new ReplaySubject(1);
|
|
7192
7192
|
_pendingParams = {};
|
|
@@ -7241,11 +7241,15 @@ class QdRouterQueryParamHubService {
|
|
|
7241
7241
|
* @param owner Stable identity of the caller — usually `this`. Used as the registry key.
|
|
7242
7242
|
* @param destroyRef Optional. If provided, the hub releases the keys automatically when the
|
|
7243
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.
|
|
7244
7248
|
* @returns `true` if every requested key is now owned by `owner`. `false` if at least one key
|
|
7245
7249
|
* was already owned by a different adapter; the registry is left untouched and `destroyRef`
|
|
7246
7250
|
* is not registered.
|
|
7247
7251
|
*/
|
|
7248
|
-
claim(keys, owner, destroyRef) {
|
|
7252
|
+
claim(keys, owner, destroyRef, featureLabel) {
|
|
7249
7253
|
const conflicts = [];
|
|
7250
7254
|
for (const key of keys) {
|
|
7251
7255
|
const existing = this._ownership.get(key);
|
|
@@ -7253,17 +7257,34 @@ class QdRouterQueryParamHubService {
|
|
|
7253
7257
|
conflicts.push(key);
|
|
7254
7258
|
}
|
|
7255
7259
|
if (conflicts.length > 0) {
|
|
7256
|
-
const
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
`different param key.`);
|
|
7260
|
+
const existingOwner = this._ownership.get(conflicts[0]);
|
|
7261
|
+
const existingLabel = this._ownerLabels.get(existingOwner);
|
|
7262
|
+
console.error(this.formatOwnershipConflictMessage(conflicts, existingLabel));
|
|
7260
7263
|
return false;
|
|
7261
7264
|
}
|
|
7262
7265
|
keys.forEach(key => this._ownership.set(key, owner));
|
|
7266
|
+
if (featureLabel)
|
|
7267
|
+
this._ownerLabels.set(owner, featureLabel);
|
|
7263
7268
|
if (destroyRef && keys.length > 0)
|
|
7264
7269
|
this.registerAutoRelease(owner, keys, destroyRef);
|
|
7265
7270
|
return true;
|
|
7266
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
|
+
}
|
|
7267
7288
|
/**
|
|
7268
7289
|
* Drops `owner`'s claim on the given keys. Keys not owned by `owner` are left untouched —
|
|
7269
7290
|
* a foreign release call cannot steal another adapter's ownership.
|
|
@@ -7322,13 +7343,10 @@ class QdRouterQueryParamHubService {
|
|
|
7322
7343
|
*
|
|
7323
7344
|
* @param params Partial map of query-param keys to their new values. Missing keys are not touched.
|
|
7324
7345
|
* An empty object short-circuits and is a no-op.
|
|
7325
|
-
* @param
|
|
7326
|
-
*
|
|
7327
|
-
* replace is preserved when a follow-up write would otherwise push). The escalation is
|
|
7328
|
-
* per-batch — after each flush the pending flag resets to `false`, so writes that arrive
|
|
7329
|
-
* in the next microtask start a fresh batch with their own `replaceUrl` argument.
|
|
7346
|
+
* @param options Write options — see `QdRouterQueryParamWriteOptions` for the `replaceUrl`
|
|
7347
|
+
* semantics and per-batch escalation rules.
|
|
7330
7348
|
*/
|
|
7331
|
-
write(params,
|
|
7349
|
+
write(params, options) {
|
|
7332
7350
|
const router = this._router;
|
|
7333
7351
|
const activatedRoute = this._activatedRoute;
|
|
7334
7352
|
if (!router || !activatedRoute)
|
|
@@ -7336,7 +7354,7 @@ class QdRouterQueryParamHubService {
|
|
|
7336
7354
|
if (Object.keys(params).length === 0)
|
|
7337
7355
|
return;
|
|
7338
7356
|
Object.assign(this._pendingParams, params);
|
|
7339
|
-
this._pendingReplaceUrl = this._pendingReplaceUrl || replaceUrl;
|
|
7357
|
+
this._pendingReplaceUrl = this._pendingReplaceUrl || options.replaceUrl;
|
|
7340
7358
|
if (this._flushScheduled)
|
|
7341
7359
|
return;
|
|
7342
7360
|
this._flushScheduled = true;
|
|
@@ -21200,99 +21218,86 @@ function unescape(escapedString) {
|
|
|
21200
21218
|
return escapedString.replace(new RegExp(`\\\\(${SERIALIZATION_SYMBOLS.map(s => '\\' + s).join('|')})`, 'g'), '$1');
|
|
21201
21219
|
}
|
|
21202
21220
|
|
|
21203
|
-
|
|
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
|
+
*/
|
|
21204
21231
|
class QdFilterRouterConnectorService {
|
|
21205
|
-
|
|
21206
|
-
|
|
21207
|
-
|
|
21232
|
+
_filterService = inject(QdFilterService);
|
|
21233
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
21234
|
+
_destroyRef = inject(DestroyRef);
|
|
21208
21235
|
_connectedFilter;
|
|
21209
|
-
|
|
21210
|
-
|
|
21211
|
-
_navigationEnded$;
|
|
21236
|
+
_readSubscription;
|
|
21237
|
+
_writeSubscription;
|
|
21212
21238
|
_activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
21213
21239
|
_activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
21214
21240
|
constructor() {
|
|
21215
|
-
|
|
21216
|
-
return;
|
|
21217
|
-
const navigationPending$ = new ReplaySubject(1);
|
|
21218
|
-
this._navigationEnded$ = navigationPending$.pipe(filter(navigationPending => !navigationPending));
|
|
21219
|
-
if (this.router.navigated)
|
|
21220
|
-
navigationPending$.next(false);
|
|
21221
|
-
this.router.events.subscribe(event => {
|
|
21222
|
-
switch (event.constructor) {
|
|
21223
|
-
case NavigationStart:
|
|
21224
|
-
navigationPending$.next(true);
|
|
21225
|
-
break;
|
|
21226
|
-
case NavigationEnd:
|
|
21227
|
-
case NavigationCancel:
|
|
21228
|
-
case NavigationError:
|
|
21229
|
-
navigationPending$.next(false);
|
|
21230
|
-
break;
|
|
21231
|
-
}
|
|
21232
|
-
});
|
|
21241
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
21233
21242
|
}
|
|
21234
21243
|
connectFilterWithRouter(filterComponent) {
|
|
21235
|
-
if (filterComponent.filterData?.connectWithRouter === false || !this.
|
|
21244
|
+
if (filterComponent.filterData?.connectWithRouter === false || !this._hub.isAvailable())
|
|
21236
21245
|
return of(false);
|
|
21237
|
-
|
|
21238
|
-
|
|
21239
|
-
|
|
21240
|
-
'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))
|
|
21241
21249
|
return of(false);
|
|
21242
|
-
}
|
|
21243
21250
|
this._connectedFilter = filterComponent;
|
|
21244
|
-
this.
|
|
21245
|
-
this.
|
|
21251
|
+
this._activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
21252
|
+
this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
21253
|
+
this.subscribeToUrlChanges();
|
|
21254
|
+
this.subscribeToFilterChanges();
|
|
21246
21255
|
return this._activatedRouteChecked$;
|
|
21247
21256
|
}
|
|
21248
|
-
|
|
21249
|
-
this.
|
|
21250
|
-
|
|
21251
|
-
|
|
21252
|
-
|
|
21253
|
-
|
|
21254
|
-
|
|
21255
|
-
|
|
21256
|
-
|
|
21257
|
-
|
|
21258
|
-
|
|
21259
|
-
|
|
21260
|
-
|
|
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)
|
|
21261
21274
|
});
|
|
21262
21275
|
}
|
|
21263
|
-
|
|
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
|
+
}
|
|
21264
21283
|
});
|
|
21265
21284
|
}
|
|
21266
|
-
|
|
21267
|
-
this.
|
|
21285
|
+
subscribeToFilterChanges() {
|
|
21286
|
+
this._writeSubscription = this._filterService
|
|
21268
21287
|
.getFilterUrlParameter$(this._connectedFilter.filterId)
|
|
21269
|
-
.pipe(switchMap(filterUrlParameter => this.
|
|
21288
|
+
.pipe(distinctUntilChanged(), switchMap(filterUrlParameter => this._hub.navigationSettled$.pipe(take(1), map(() => filterUrlParameter))), takeUntilDestroyed(this._destroyRef))
|
|
21270
21289
|
.subscribe(filterUrlParameter => {
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
},
|
|
21276
|
-
queryParamsHandling: 'merge',
|
|
21277
|
-
replaceUrl: true
|
|
21278
|
-
});
|
|
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 });
|
|
21279
21294
|
});
|
|
21280
21295
|
}
|
|
21281
|
-
disconnectFilterFromRouter(filterComponent) {
|
|
21282
|
-
if (this._connectedFilter !== filterComponent)
|
|
21283
|
-
return;
|
|
21284
|
-
this._connectedFilter = null;
|
|
21285
|
-
this._activatedRouteSubscription?.unsubscribe();
|
|
21286
|
-
this._filterUrlParameterSubscription?.unsubscribe();
|
|
21287
|
-
}
|
|
21288
21296
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
21289
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService
|
|
21297
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService });
|
|
21290
21298
|
}
|
|
21291
21299
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService, decorators: [{
|
|
21292
|
-
type: Injectable
|
|
21293
|
-
args: [{
|
|
21294
|
-
providedIn: 'root'
|
|
21295
|
-
}]
|
|
21300
|
+
type: Injectable
|
|
21296
21301
|
}], ctorParameters: () => [] });
|
|
21297
21302
|
|
|
21298
21303
|
class QdDependentFiltersService {
|
|
@@ -21432,7 +21437,7 @@ class QdFilterComponent {
|
|
|
21432
21437
|
_filterDataSubject = new BehaviorSubject(undefined);
|
|
21433
21438
|
_hasPreselectionSubject = new ReplaySubject(1);
|
|
21434
21439
|
_categoriesSubject = new ReplaySubject(1);
|
|
21435
|
-
_filterConnectedWithRouterSubject = new ReplaySubject();
|
|
21440
|
+
_filterConnectedWithRouterSubject = new ReplaySubject(1);
|
|
21436
21441
|
_filterConnectedWithRouter$ = this._filterConnectedWithRouterSubject.asObservable();
|
|
21437
21442
|
set hasPreselection(hasPreselection) {
|
|
21438
21443
|
this._hasPreselectionSubject.next(hasPreselection);
|
|
@@ -21470,7 +21475,6 @@ class QdFilterComponent {
|
|
|
21470
21475
|
}
|
|
21471
21476
|
}
|
|
21472
21477
|
ngOnDestroy() {
|
|
21473
|
-
this.filterRouterConnectorService.disconnectFilterFromRouter(this);
|
|
21474
21478
|
this.dependentFiltersService.destroy();
|
|
21475
21479
|
this._queryStringSubscription?.unsubscribe();
|
|
21476
21480
|
this._postBodySubscription?.unsubscribe();
|
|
@@ -21605,11 +21609,11 @@ class QdFilterComponent {
|
|
|
21605
21609
|
});
|
|
21606
21610
|
}
|
|
21607
21611
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
21608
|
-
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 });
|
|
21609
21613
|
}
|
|
21610
21614
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterComponent, decorators: [{
|
|
21611
21615
|
type: Component,
|
|
21612
|
-
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"] }]
|
|
21613
21617
|
}], propDecorators: { filterData: [{
|
|
21614
21618
|
type: Input
|
|
21615
21619
|
}], testId: [{
|
|
@@ -22277,94 +22281,88 @@ function searchReducer(state, action) {
|
|
|
22277
22281
|
}
|
|
22278
22282
|
|
|
22279
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
|
+
*/
|
|
22280
22295
|
class QdSearchRouterConnectorService {
|
|
22281
|
-
|
|
22282
|
-
|
|
22296
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
22297
|
+
_destroyRef = inject(DestroyRef);
|
|
22283
22298
|
_connectedSearch;
|
|
22284
|
-
|
|
22285
|
-
|
|
22286
|
-
_navigationEnded$;
|
|
22299
|
+
_readSubscription;
|
|
22300
|
+
_writeSubscription;
|
|
22287
22301
|
_activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
22288
22302
|
_activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
22289
22303
|
constructor() {
|
|
22290
|
-
|
|
22291
|
-
return;
|
|
22292
|
-
const navigationPending$ = new ReplaySubject(1);
|
|
22293
|
-
this._navigationEnded$ = navigationPending$.pipe(filter(navigationPending => !navigationPending));
|
|
22294
|
-
if (this.router.navigated)
|
|
22295
|
-
navigationPending$.next(false);
|
|
22296
|
-
this.router.events.subscribe(event => {
|
|
22297
|
-
switch (event.constructor) {
|
|
22298
|
-
case NavigationStart:
|
|
22299
|
-
navigationPending$.next(true);
|
|
22300
|
-
break;
|
|
22301
|
-
case NavigationEnd:
|
|
22302
|
-
case NavigationCancel:
|
|
22303
|
-
case NavigationError:
|
|
22304
|
-
navigationPending$.next(false);
|
|
22305
|
-
break;
|
|
22306
|
-
}
|
|
22307
|
-
});
|
|
22304
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
22308
22305
|
}
|
|
22309
22306
|
connectSearchWithRouter(searchComponent) {
|
|
22310
|
-
if (searchComponent.configData?.connectWithRouter !== true || !this.
|
|
22307
|
+
if (searchComponent.configData?.connectWithRouter !== true || !this._hub.isAvailable())
|
|
22311
22308
|
return of(false);
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
'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))
|
|
22316
22312
|
return of(false);
|
|
22317
|
-
}
|
|
22318
22313
|
this._connectedSearch = searchComponent;
|
|
22319
|
-
this.
|
|
22320
|
-
this.
|
|
22314
|
+
this._activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
22315
|
+
this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
22316
|
+
this.subscribeToUrlChanges();
|
|
22317
|
+
this.subscribeToSearchChanges();
|
|
22321
22318
|
return this._activatedRouteChecked$;
|
|
22322
22319
|
}
|
|
22323
|
-
|
|
22324
|
-
this.
|
|
22325
|
-
|
|
22326
|
-
|
|
22327
|
-
|
|
22328
|
-
|
|
22329
|
-
|
|
22330
|
-
|
|
22331
|
-
|
|
22332
|
-
|
|
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 = '';
|
|
22340
|
+
}
|
|
22341
|
+
if (isFirstEmit) {
|
|
22342
|
+
this._activatedRouteCheckedSubject.next(true);
|
|
22343
|
+
isFirstEmit = false;
|
|
22333
22344
|
}
|
|
22334
|
-
|
|
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 });
|
|
22335
22354
|
});
|
|
22336
22355
|
}
|
|
22337
22356
|
getNotEscapedSplitRegExp() {
|
|
22338
22357
|
return new RegExp('(?<!\\\\)\\' + PHRASE_PRESELECT_SEPARATOR);
|
|
22339
22358
|
}
|
|
22340
|
-
|
|
22341
|
-
|
|
22342
|
-
.pipe(switchMap(searchData => this._navigationEnded$.pipe(take$1(1), map(() => searchData))))
|
|
22343
|
-
.subscribe(({ phrase, preSelect }) => {
|
|
22344
|
-
this.router.navigate([], {
|
|
22345
|
-
relativeTo: this.activatedRoute,
|
|
22346
|
-
queryParams: {
|
|
22347
|
-
search: this.escape(phrase) + (preSelect ? PHRASE_PRESELECT_SEPARATOR + this.escape(preSelect) : '')
|
|
22348
|
-
},
|
|
22349
|
-
queryParamsHandling: 'merge',
|
|
22350
|
-
replaceUrl: true
|
|
22351
|
-
});
|
|
22352
|
-
});
|
|
22353
|
-
}
|
|
22354
|
-
escape(string) {
|
|
22355
|
-
if (!string)
|
|
22359
|
+
escape(value) {
|
|
22360
|
+
if (!value)
|
|
22356
22361
|
return '';
|
|
22357
|
-
return
|
|
22358
|
-
}
|
|
22359
|
-
unescape(escapedString) {
|
|
22360
|
-
return escapedString.replace(new RegExp(`\\\\(${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '$1');
|
|
22362
|
+
return value.replace(new RegExp(`(\\${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '\\$1');
|
|
22361
22363
|
}
|
|
22362
|
-
|
|
22363
|
-
|
|
22364
|
-
return;
|
|
22365
|
-
this._connectedSearch = null;
|
|
22366
|
-
this._activatedRouteSubscription?.unsubscribe();
|
|
22367
|
-
this._searchUrlParameterSubscription?.unsubscribe();
|
|
22364
|
+
unescape(escapedValue) {
|
|
22365
|
+
return escapedValue.replace(new RegExp(`\\\\(${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '$1');
|
|
22368
22366
|
}
|
|
22369
22367
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdSearchRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
22370
22368
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdSearchRouterConnectorService });
|
|
@@ -22561,7 +22559,7 @@ class QdSearchComponent {
|
|
|
22561
22559
|
_search = '';
|
|
22562
22560
|
_preSelect = '';
|
|
22563
22561
|
_destroyed$ = new Subject();
|
|
22564
|
-
_searchConnectedWithRouterSubject = new ReplaySubject();
|
|
22562
|
+
_searchConnectedWithRouterSubject = new ReplaySubject(1);
|
|
22565
22563
|
_searchConnectedWithRouter$ = this._searchConnectedWithRouterSubject.asObservable();
|
|
22566
22564
|
get inputConfig() {
|
|
22567
22565
|
return {
|
|
@@ -22644,7 +22642,6 @@ class QdSearchComponent {
|
|
|
22644
22642
|
}
|
|
22645
22643
|
}
|
|
22646
22644
|
ngOnDestroy() {
|
|
22647
|
-
this.searchRouterConnectorService.disconnectSearchFromRouter(this);
|
|
22648
22645
|
this._destroyed$.next();
|
|
22649
22646
|
this._destroyed$.complete();
|
|
22650
22647
|
}
|
|
@@ -23655,11 +23652,26 @@ var QdPaginatorDirection;
|
|
|
23655
23652
|
|
|
23656
23653
|
const PAGE_PARAM_NAME = 'page';
|
|
23657
23654
|
const SIZE_PARAM_NAME = 'size';
|
|
23658
|
-
const OWNED_PARAMS$
|
|
23655
|
+
const OWNED_PARAMS$2 = [PAGE_PARAM_NAME, SIZE_PARAM_NAME];
|
|
23656
|
+
const FEATURE_LABEL$2 = { name: 'Table', plural: 'tables' };
|
|
23659
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
|
+
*/
|
|
23660
23672
|
class QdTablePaginationRouterConnectorService {
|
|
23661
|
-
|
|
23662
|
-
|
|
23673
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
23674
|
+
_destroyRef = inject(DestroyRef);
|
|
23663
23675
|
_connection;
|
|
23664
23676
|
_readSubscription;
|
|
23665
23677
|
_writeSubscription;
|
|
@@ -23667,15 +23679,12 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23667
23679
|
_activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
23668
23680
|
_hasInitialUrlBeenSet = false;
|
|
23669
23681
|
constructor() {
|
|
23670
|
-
this.
|
|
23671
|
-
this.tearDownConnection();
|
|
23672
|
-
this.hub.release(OWNED_PARAMS$1, this);
|
|
23673
|
-
});
|
|
23682
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
23674
23683
|
}
|
|
23675
23684
|
connectPaginationWithRouter(paginator, tableStoreService) {
|
|
23676
|
-
if (!paginator.shouldConnectWithRouter() || !this.
|
|
23685
|
+
if (!paginator.shouldConnectWithRouter() || !this._hub.isAvailable())
|
|
23677
23686
|
return of(false);
|
|
23678
|
-
if (!this.
|
|
23687
|
+
if (!this._hub.claim(OWNED_PARAMS$2, this, this._destroyRef, FEATURE_LABEL$2))
|
|
23679
23688
|
return of(false);
|
|
23680
23689
|
this._connection = {
|
|
23681
23690
|
paginator: paginator,
|
|
@@ -23684,15 +23693,32 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23684
23693
|
this._hasInitialUrlBeenSet = false;
|
|
23685
23694
|
this._activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
23686
23695
|
this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
23696
|
+
this.applyInitialUrlSynchronously();
|
|
23687
23697
|
this.subscribeToUrlChanges();
|
|
23688
23698
|
this.subscribeToStoreChanges();
|
|
23689
23699
|
return this._activatedRouteChecked$;
|
|
23690
23700
|
}
|
|
23691
|
-
|
|
23692
|
-
|
|
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)
|
|
23693
23720
|
return;
|
|
23694
|
-
|
|
23695
|
-
this.hub.release(OWNED_PARAMS$1, this);
|
|
23721
|
+
tableStoreService.setPageParams(pageIndex, pageSize, currentTotalCount);
|
|
23696
23722
|
}
|
|
23697
23723
|
tearDownConnection() {
|
|
23698
23724
|
this._connection = undefined;
|
|
@@ -23703,8 +23729,8 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23703
23729
|
subscribeToUrlChanges() {
|
|
23704
23730
|
const { tableStoreService } = this._connection;
|
|
23705
23731
|
let isFirstEmit = true;
|
|
23706
|
-
this._readSubscription = this.
|
|
23707
|
-
.pipe(switchMap(() => this.
|
|
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 })))))
|
|
23708
23734
|
.subscribe(({ params, currentInfo, totalCount }) => {
|
|
23709
23735
|
const target = this.toStoreState(params);
|
|
23710
23736
|
const isChanged = !currentInfo || currentInfo.pageIndex !== target.pageIndex || currentInfo.pageSize !== target.pageSize;
|
|
@@ -23721,7 +23747,7 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23721
23747
|
const { tableStoreService } = this._connection;
|
|
23722
23748
|
this._writeSubscription = tableStoreService
|
|
23723
23749
|
.pageChangeInfo$()
|
|
23724
|
-
.pipe(filter((info) => info !== undefined), distinctUntilChanged((a, b) => a.pageIndex === b.pageIndex && a.pageSize === b.pageSize), switchMap(info => this.
|
|
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))))
|
|
23725
23751
|
.subscribe(info => this.writeUrl(info.pageIndex, info.pageSize));
|
|
23726
23752
|
}
|
|
23727
23753
|
writeUrl(pageIndex, pageSize) {
|
|
@@ -23729,11 +23755,11 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23729
23755
|
this._hasInitialUrlBeenSet = true;
|
|
23730
23756
|
const targetPage = String(pageIndex + 1);
|
|
23731
23757
|
const targetSize = String(pageSize);
|
|
23732
|
-
const urlAlreadyMatches = this.
|
|
23733
|
-
this.
|
|
23758
|
+
const urlAlreadyMatches = this._hub.snapshotQueryParam(PAGE_PARAM_NAME) === targetPage &&
|
|
23759
|
+
this._hub.snapshotQueryParam(SIZE_PARAM_NAME) === targetSize;
|
|
23734
23760
|
if (urlAlreadyMatches)
|
|
23735
23761
|
return;
|
|
23736
|
-
this.
|
|
23762
|
+
this._hub.write({ [PAGE_PARAM_NAME]: targetPage, [SIZE_PARAM_NAME]: targetSize }, { replaceUrl });
|
|
23737
23763
|
}
|
|
23738
23764
|
parseUrlParams(queryParams) {
|
|
23739
23765
|
const result = {};
|
|
@@ -23754,9 +23780,8 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23754
23780
|
}
|
|
23755
23781
|
isValidSize(value) {
|
|
23756
23782
|
const pageSizes = this.getConfiguredPageSizes();
|
|
23757
|
-
if (pageSizes && pageSizes.length > 0)
|
|
23783
|
+
if (pageSizes && pageSizes.length > 0)
|
|
23758
23784
|
return pageSizes.includes(value);
|
|
23759
|
-
}
|
|
23760
23785
|
return value <= SIZE_SANITY_MAX;
|
|
23761
23786
|
}
|
|
23762
23787
|
getConfiguredPageSizes() {
|
|
@@ -24045,7 +24070,6 @@ class QdTablePaginatorComponent {
|
|
|
24045
24070
|
this.startPagination();
|
|
24046
24071
|
}
|
|
24047
24072
|
ngOnDestroy() {
|
|
24048
|
-
this.routerConnector.disconnectPaginationFromRouter(this);
|
|
24049
24073
|
this._destroyed$.next(null);
|
|
24050
24074
|
this._destroyed$.complete();
|
|
24051
24075
|
}
|
|
@@ -24145,12 +24169,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
24145
24169
|
}] } });
|
|
24146
24170
|
|
|
24147
24171
|
const SORT_PARAM_NAME = 'sort';
|
|
24148
|
-
const OWNED_PARAMS = [SORT_PARAM_NAME];
|
|
24172
|
+
const OWNED_PARAMS$1 = [SORT_PARAM_NAME];
|
|
24173
|
+
const FEATURE_LABEL$1 = { name: 'Table', plural: 'tables' };
|
|
24149
24174
|
const SEGMENT_SEPARATOR = ',';
|
|
24150
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
|
+
*/
|
|
24151
24190
|
class QdTableSortRouterConnectorService {
|
|
24152
|
-
|
|
24153
|
-
|
|
24191
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
24192
|
+
_destroyRef = inject(DestroyRef);
|
|
24154
24193
|
_connection;
|
|
24155
24194
|
_readSubscription;
|
|
24156
24195
|
_writeSubscription;
|
|
@@ -24159,15 +24198,12 @@ class QdTableSortRouterConnectorService {
|
|
|
24159
24198
|
_hasInitialUrlBeenSet = false;
|
|
24160
24199
|
_hasWarnedMultiSegment = false;
|
|
24161
24200
|
constructor() {
|
|
24162
|
-
this.
|
|
24163
|
-
this.tearDownConnection();
|
|
24164
|
-
this.hub.release(OWNED_PARAMS, this);
|
|
24165
|
-
});
|
|
24201
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
24166
24202
|
}
|
|
24167
24203
|
connectSortWithRouter(table, tableStoreService) {
|
|
24168
|
-
if (!this.shouldConnect(table) || !this.
|
|
24204
|
+
if (!this.shouldConnect(table) || !this._hub.isAvailable())
|
|
24169
24205
|
return of(false);
|
|
24170
|
-
if (!this.
|
|
24206
|
+
if (!this._hub.claim(OWNED_PARAMS$1, this, this._destroyRef, FEATURE_LABEL$1))
|
|
24171
24207
|
return of(false);
|
|
24172
24208
|
this._connection = {
|
|
24173
24209
|
table: table,
|
|
@@ -24182,12 +24218,6 @@ class QdTableSortRouterConnectorService {
|
|
|
24182
24218
|
this.subscribeToStoreChanges();
|
|
24183
24219
|
return this._activatedRouteChecked$;
|
|
24184
24220
|
}
|
|
24185
|
-
disconnectSortFromRouter(table) {
|
|
24186
|
-
if (this._connection?.table !== table)
|
|
24187
|
-
return;
|
|
24188
|
-
this.tearDownConnection();
|
|
24189
|
-
this.hub.release(OWNED_PARAMS, this);
|
|
24190
|
-
}
|
|
24191
24221
|
tearDownConnection() {
|
|
24192
24222
|
this._connection = undefined;
|
|
24193
24223
|
this._hasInitialUrlBeenSet = false;
|
|
@@ -24198,12 +24228,14 @@ class QdTableSortRouterConnectorService {
|
|
|
24198
24228
|
subscribeToUrlChanges() {
|
|
24199
24229
|
const { tableStoreService } = this._connection;
|
|
24200
24230
|
let isFirstEmit = true;
|
|
24201
|
-
this._readSubscription = this.
|
|
24202
|
-
.pipe(switchMap(() => this.
|
|
24203
|
-
.subscribe(segments => {
|
|
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]) => {
|
|
24204
24234
|
if (segments.length === 1) {
|
|
24205
24235
|
const [{ column, direction }] = segments;
|
|
24206
|
-
|
|
24236
|
+
if (!this.isSameAsCurrentSort(currentSort, column, direction)) {
|
|
24237
|
+
tableStoreService.setSort(column, direction);
|
|
24238
|
+
}
|
|
24207
24239
|
}
|
|
24208
24240
|
if (isFirstEmit) {
|
|
24209
24241
|
this._activatedRouteCheckedSubject.next(true);
|
|
@@ -24211,19 +24243,25 @@ class QdTableSortRouterConnectorService {
|
|
|
24211
24243
|
}
|
|
24212
24244
|
});
|
|
24213
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
|
+
}
|
|
24214
24252
|
subscribeToStoreChanges() {
|
|
24215
24253
|
const { tableStoreService } = this._connection;
|
|
24216
24254
|
this._writeSubscription = tableStoreService
|
|
24217
24255
|
.tableSort$()
|
|
24218
|
-
.pipe(filter((sort) => Array.isArray(sort)), map(sort => this.serializeSort(sort)), distinctUntilChanged(), switchMap(serialized => this.
|
|
24256
|
+
.pipe(filter((sort) => Array.isArray(sort)), map(sort => this.serializeSort(sort)), distinctUntilChanged(), switchMap(serialized => this._hub.navigationSettled$.pipe(take(1), map(() => serialized))))
|
|
24219
24257
|
.subscribe(serialized => this.writeUrl(serialized));
|
|
24220
24258
|
}
|
|
24221
24259
|
writeUrl(serialized) {
|
|
24222
24260
|
const replaceUrl = !this._hasInitialUrlBeenSet;
|
|
24223
24261
|
this._hasInitialUrlBeenSet = true;
|
|
24224
|
-
if (this.
|
|
24262
|
+
if (this._hub.snapshotQueryParam(SORT_PARAM_NAME) === serialized)
|
|
24225
24263
|
return;
|
|
24226
|
-
this.
|
|
24264
|
+
this._hub.write({ [SORT_PARAM_NAME]: serialized }, { replaceUrl });
|
|
24227
24265
|
}
|
|
24228
24266
|
shouldConnect(table) {
|
|
24229
24267
|
const sortConfig = table.config.sort;
|
|
@@ -25039,7 +25077,6 @@ class QdTableComponent {
|
|
|
25039
25077
|
}
|
|
25040
25078
|
}
|
|
25041
25079
|
ngOnDestroy() {
|
|
25042
|
-
this.sortRouterConnector.disconnectSortFromRouter(this);
|
|
25043
25080
|
this.tableStoreService.updateTableStateRecentSecondaryAction(undefined);
|
|
25044
25081
|
this.tableStoreService.resetConnectorStates();
|
|
25045
25082
|
this.fillingWidthService.destroy();
|
|
@@ -29174,6 +29211,149 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
29174
29211
|
args: ['data-test-id']
|
|
29175
29212
|
}] } });
|
|
29176
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
|
+
|
|
29177
29357
|
/**
|
|
29178
29358
|
* **QdPageTabsComponent** provides a non-linear tabbed navigation system within a **QdPage**.
|
|
29179
29359
|
* It enables switching between different sections while maintaining form validation and controlled navigation.
|
|
@@ -29211,13 +29391,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
29211
29391
|
*
|
|
29212
29392
|
* #### **Submit Button Configuration**
|
|
29213
29393
|
*
|
|
29214
|
-
* 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`:
|
|
29215
29395
|
*
|
|
29216
29396
|
* - **i18n**: The translated label for the submit button.
|
|
29217
29397
|
* - **handler**: A callback function that receives form data upon submission.
|
|
29218
29398
|
* - **isDisabled**: Controls whether the button should be active or not.
|
|
29219
29399
|
* - **isHidden**: Determines whether the button is visible.
|
|
29220
|
-
*
|
|
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`.
|
|
29221
29404
|
*
|
|
29222
29405
|
* #### **Usage**
|
|
29223
29406
|
*
|
|
@@ -29336,8 +29519,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
29336
29519
|
class QdPageTabsComponent extends CdkStepper {
|
|
29337
29520
|
footerService = inject(QdPageFooterService, { optional: true });
|
|
29338
29521
|
pageStoreService = inject(QdPageStoreService);
|
|
29339
|
-
|
|
29340
|
-
route = inject(ActivatedRoute, { optional: true });
|
|
29522
|
+
routerConnector = inject(QdPageTabsRouterConnectorService);
|
|
29341
29523
|
/**
|
|
29342
29524
|
* Configuration of QdPageTabs.
|
|
29343
29525
|
*/
|
|
@@ -29396,8 +29578,11 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29396
29578
|
ngAfterViewInit() {
|
|
29397
29579
|
super.ngAfterViewInit();
|
|
29398
29580
|
setTimeout(() => {
|
|
29581
|
+
if (this.config?.connectWithRouter) {
|
|
29582
|
+
this.configureBookmarkableTabs();
|
|
29583
|
+
return;
|
|
29584
|
+
}
|
|
29399
29585
|
this.initializeTabSelection();
|
|
29400
|
-
this.configureBookmarkableTabs();
|
|
29401
29586
|
});
|
|
29402
29587
|
}
|
|
29403
29588
|
initializeTabSelection() {
|
|
@@ -29408,54 +29593,7 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29408
29593
|
this.selectFirstNotDisabledTab();
|
|
29409
29594
|
}
|
|
29410
29595
|
configureBookmarkableTabs() {
|
|
29411
|
-
|
|
29412
|
-
this.initializeTabFromUrl();
|
|
29413
|
-
this.initializeFirstTabSelection();
|
|
29414
|
-
}
|
|
29415
|
-
}
|
|
29416
|
-
/**
|
|
29417
|
-
* Initializes the tab selection based on the URL parameter 'tab'.
|
|
29418
|
-
* @private
|
|
29419
|
-
*/
|
|
29420
|
-
initializeTabFromUrl() {
|
|
29421
|
-
this.route.queryParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(params => {
|
|
29422
|
-
const tabNameFromParams = params['tab'];
|
|
29423
|
-
const pageTab = this.tabs.find(tab => tab.config?.name === tabNameFromParams);
|
|
29424
|
-
if (pageTab) {
|
|
29425
|
-
pageTab.select();
|
|
29426
|
-
}
|
|
29427
|
-
else {
|
|
29428
|
-
console.warn('Quadrel Framework | QdPageTabs - No tab found with name "' + tabNameFromParams + '".');
|
|
29429
|
-
this.selectFirstNotDisabledTab(true);
|
|
29430
|
-
}
|
|
29431
|
-
});
|
|
29432
|
-
}
|
|
29433
|
-
/**
|
|
29434
|
-
* If the user navigates to a page with a tab selected, the tab will be selected automatically in {@link initializeTabFromUrl()} method.
|
|
29435
|
-
* If the user navigates to a page without a tab selected, the first active tab will be selected.
|
|
29436
|
-
* @private
|
|
29437
|
-
*/
|
|
29438
|
-
initializeFirstTabSelection() {
|
|
29439
|
-
this.route.queryParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(params => {
|
|
29440
|
-
const tabNameFromParams = params['tab'];
|
|
29441
|
-
if (!tabNameFromParams) {
|
|
29442
|
-
const selectedIndex = this.config.selectedIndex;
|
|
29443
|
-
const pageTab = this.tabs.find((tab, tabIndex) => tabIndex === selectedIndex);
|
|
29444
|
-
if (pageTab && pageTab?.config.name) {
|
|
29445
|
-
this.router.navigate([], {
|
|
29446
|
-
relativeTo: this.route,
|
|
29447
|
-
queryParams: { tab: pageTab.config.name },
|
|
29448
|
-
queryParamsHandling: 'merge'
|
|
29449
|
-
});
|
|
29450
|
-
}
|
|
29451
|
-
else {
|
|
29452
|
-
console.warn('Quadrel Framework | QdPageTabs - "connectedWithRouter" is active, however <qd-page-tab> has no "name" attribute.');
|
|
29453
|
-
}
|
|
29454
|
-
}
|
|
29455
|
-
});
|
|
29456
|
-
}
|
|
29457
|
-
ngOnDestroy() {
|
|
29458
|
-
super.ngOnDestroy();
|
|
29596
|
+
this.routerConnector.connectTabsWithRouter(this).pipe(takeUntilDestroyed(this.destroyRef)).subscribe();
|
|
29459
29597
|
}
|
|
29460
29598
|
isTabSelectable(index) {
|
|
29461
29599
|
const tab = this.tabs.get(index);
|
|
@@ -29463,22 +29601,14 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29463
29601
|
}
|
|
29464
29602
|
/**
|
|
29465
29603
|
* Selects the first tab that is not disabled.
|
|
29466
|
-
* @param updateUrl if true, the URL will be updated to reflect the selected tab.
|
|
29467
29604
|
*/
|
|
29468
|
-
selectFirstNotDisabledTab(
|
|
29605
|
+
selectFirstNotDisabledTab() {
|
|
29469
29606
|
this.tabs.some((tab, tabIndex) => {
|
|
29470
29607
|
if (!tab.config.isDisabled) {
|
|
29471
29608
|
if (this.config) {
|
|
29472
29609
|
this.config.selectedIndex = tabIndex;
|
|
29473
29610
|
}
|
|
29474
29611
|
this.selectedIndex = tabIndex;
|
|
29475
|
-
if (updateUrl && this.config?.connectWithRouter && tab.config?.name) {
|
|
29476
|
-
this.router.navigate([], {
|
|
29477
|
-
relativeTo: this.route,
|
|
29478
|
-
queryParams: { tab: tab.config.name },
|
|
29479
|
-
queryParamsHandling: 'merge'
|
|
29480
|
-
});
|
|
29481
|
-
}
|
|
29482
29612
|
return true;
|
|
29483
29613
|
}
|
|
29484
29614
|
else {
|
|
@@ -29495,13 +29625,6 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29495
29625
|
if (tab.config?.isDisabled)
|
|
29496
29626
|
return;
|
|
29497
29627
|
tab.select();
|
|
29498
|
-
if (this.config?.connectWithRouter && tab.config?.name) {
|
|
29499
|
-
this.router.navigate([], {
|
|
29500
|
-
relativeTo: this.route,
|
|
29501
|
-
queryParams: { tab: tab.config.name },
|
|
29502
|
-
queryParamsHandling: 'merge'
|
|
29503
|
-
});
|
|
29504
|
-
}
|
|
29505
29628
|
}
|
|
29506
29629
|
isSubmitButtonShown() {
|
|
29507
29630
|
return this.config?.submitButton?.isHidden !== true && !this.footerService;
|
|
@@ -29526,11 +29649,11 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29526
29649
|
});
|
|
29527
29650
|
}
|
|
29528
29651
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
29529
|
-
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 });
|
|
29530
29653
|
}
|
|
29531
29654
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsComponent, decorators: [{
|
|
29532
29655
|
type: Component,
|
|
29533
|
-
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"] }]
|
|
29534
29657
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
29535
29658
|
type: Input
|
|
29536
29659
|
}], testId: [{
|
|
@@ -31925,7 +32048,7 @@ class QdShellHeaderComponent {
|
|
|
31925
32048
|
const navigationService = this.navigationService;
|
|
31926
32049
|
const shellLeftService = this.shellLeftService;
|
|
31927
32050
|
const shellRightService = this.shellRightService;
|
|
31928
|
-
this.backLinkDisplayed$ = navigationService.getPreviousHref$().pipe(withLatestFrom(navigationService.getRouteComponentInstance$()), switchMap(([previousHref, routeComponentInstance]) => {
|
|
32051
|
+
this.backLinkDisplayed$ = navigationService.getPreviousHref$().pipe(withLatestFrom$1(navigationService.getRouteComponentInstance$()), switchMap(([previousHref, routeComponentInstance]) => {
|
|
31929
32052
|
if (typeof previousHref === 'function')
|
|
31930
32053
|
previousHref = previousHref(routeComponentInstance);
|
|
31931
32054
|
return isObservable(previousHref) ? previousHref.pipe(take(1)) : of(previousHref);
|