@quadrel-enterprise-ui/framework 20.12.0 → 20.14.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;
|
|
@@ -19059,7 +19077,12 @@ class QdFileTypeService {
|
|
|
19059
19077
|
XLSX: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
19060
19078
|
XLSM: 'application/vnd.ms-excel.sheet.macroenabled.12',
|
|
19061
19079
|
ODS: 'application/vnd.oasis.opendocument.spreadsheet',
|
|
19062
|
-
CSV:
|
|
19080
|
+
CSV: [
|
|
19081
|
+
'text/csv',
|
|
19082
|
+
'application/csv',
|
|
19083
|
+
// Workaround for Firefox + Windows + Excel. See Excel's Windows registry entry [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.csv\Content Type]
|
|
19084
|
+
'application/vnd.ms-excel'
|
|
19085
|
+
],
|
|
19063
19086
|
PPT: [
|
|
19064
19087
|
'application/powerpoint',
|
|
19065
19088
|
'application/mspowerpoint',
|
|
@@ -21200,99 +21223,86 @@ function unescape(escapedString) {
|
|
|
21200
21223
|
return escapedString.replace(new RegExp(`\\\\(${SERIALIZATION_SYMBOLS.map(s => '\\' + s).join('|')})`, 'g'), '$1');
|
|
21201
21224
|
}
|
|
21202
21225
|
|
|
21203
|
-
|
|
21226
|
+
const FILTER_PARAM_NAME = 'filter';
|
|
21227
|
+
const OWNED_PARAMS$4 = [FILTER_PARAM_NAME];
|
|
21228
|
+
const FEATURE_LABEL$4 = { name: 'Filter', plural: 'filters' };
|
|
21229
|
+
/**
|
|
21230
|
+
* Per-view adapter that syncs `QdFilterComponent` selection with the URL `?filter=` query
|
|
21231
|
+
* param via `QdRouterQueryParamHubService`. Reads/parses on activation, writes on selection
|
|
21232
|
+
* change. Coordination, ownership, and write batching are delegated to the hub — see its
|
|
21233
|
+
* JSDoc for details. Behavior contracts live in `filter-router-connector.service.spec.ts`
|
|
21234
|
+
* and `router-query-param-hub.integration.cy.ts`.
|
|
21235
|
+
*/
|
|
21204
21236
|
class QdFilterRouterConnectorService {
|
|
21205
|
-
|
|
21206
|
-
|
|
21207
|
-
|
|
21237
|
+
_filterService = inject(QdFilterService);
|
|
21238
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
21239
|
+
_destroyRef = inject(DestroyRef);
|
|
21208
21240
|
_connectedFilter;
|
|
21209
|
-
|
|
21210
|
-
|
|
21211
|
-
_navigationEnded$;
|
|
21241
|
+
_readSubscription;
|
|
21242
|
+
_writeSubscription;
|
|
21212
21243
|
_activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
21213
21244
|
_activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
21214
21245
|
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
|
-
});
|
|
21246
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
21233
21247
|
}
|
|
21234
21248
|
connectFilterWithRouter(filterComponent) {
|
|
21235
|
-
if (filterComponent.filterData?.connectWithRouter === false || !this.
|
|
21249
|
+
if (filterComponent.filterData?.connectWithRouter === false || !this._hub.isAvailable())
|
|
21236
21250
|
return of(false);
|
|
21237
|
-
|
|
21238
|
-
|
|
21239
|
-
|
|
21240
|
-
'Please set connectWithRouter to false except for one filter.');
|
|
21251
|
+
if (this._connectedFilter === filterComponent)
|
|
21252
|
+
return this._activatedRouteChecked$;
|
|
21253
|
+
if (!this._hub.claim(OWNED_PARAMS$4, this, this._destroyRef, FEATURE_LABEL$4))
|
|
21241
21254
|
return of(false);
|
|
21242
|
-
}
|
|
21243
21255
|
this._connectedFilter = filterComponent;
|
|
21244
|
-
this.
|
|
21245
|
-
this.
|
|
21256
|
+
this._activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
21257
|
+
this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
21258
|
+
this.subscribeToUrlChanges();
|
|
21259
|
+
this.subscribeToFilterChanges();
|
|
21246
21260
|
return this._activatedRouteChecked$;
|
|
21247
21261
|
}
|
|
21248
|
-
|
|
21249
|
-
this.
|
|
21250
|
-
|
|
21251
|
-
|
|
21252
|
-
|
|
21253
|
-
|
|
21254
|
-
|
|
21255
|
-
|
|
21256
|
-
|
|
21257
|
-
|
|
21258
|
-
|
|
21259
|
-
|
|
21260
|
-
|
|
21262
|
+
tearDownConnection() {
|
|
21263
|
+
this._connectedFilter = undefined;
|
|
21264
|
+
this._readSubscription?.unsubscribe();
|
|
21265
|
+
this._writeSubscription?.unsubscribe();
|
|
21266
|
+
}
|
|
21267
|
+
subscribeToUrlChanges() {
|
|
21268
|
+
let isFirstEmit = true;
|
|
21269
|
+
this._readSubscription = this._hub.navigationSettled$
|
|
21270
|
+
.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))
|
|
21271
|
+
.subscribe(({ rawFilter, filterData }) => {
|
|
21272
|
+
const emptySelection = filterData
|
|
21273
|
+
.map(category => ({ [category.category]: [] }))
|
|
21274
|
+
.reduce((accumulator, emptyCategory) => ({ ...accumulator, ...emptyCategory }), {});
|
|
21275
|
+
if (typeof rawFilter === 'string') {
|
|
21276
|
+
this._filterService.setFilterSelection(this._connectedFilter.filterId, {
|
|
21277
|
+
...emptySelection,
|
|
21278
|
+
...parseFilterUrlParameter(rawFilter)
|
|
21261
21279
|
});
|
|
21262
21280
|
}
|
|
21263
|
-
|
|
21281
|
+
else if (!isFirstEmit) {
|
|
21282
|
+
this._filterService.setFilterSelection(this._connectedFilter.filterId, emptySelection);
|
|
21283
|
+
}
|
|
21284
|
+
if (isFirstEmit) {
|
|
21285
|
+
this._activatedRouteCheckedSubject.next(true);
|
|
21286
|
+
isFirstEmit = false;
|
|
21287
|
+
}
|
|
21264
21288
|
});
|
|
21265
21289
|
}
|
|
21266
|
-
|
|
21267
|
-
this.
|
|
21290
|
+
subscribeToFilterChanges() {
|
|
21291
|
+
this._writeSubscription = this._filterService
|
|
21268
21292
|
.getFilterUrlParameter$(this._connectedFilter.filterId)
|
|
21269
|
-
.pipe(switchMap(filterUrlParameter => this.
|
|
21293
|
+
.pipe(distinctUntilChanged(), switchMap(filterUrlParameter => this._hub.navigationSettled$.pipe(take(1), map(() => filterUrlParameter))), takeUntilDestroyed(this._destroyRef))
|
|
21270
21294
|
.subscribe(filterUrlParameter => {
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
},
|
|
21276
|
-
queryParamsHandling: 'merge',
|
|
21277
|
-
replaceUrl: true
|
|
21278
|
-
});
|
|
21295
|
+
const target = filterUrlParameter || undefined;
|
|
21296
|
+
if (this._hub.snapshotQueryParam(FILTER_PARAM_NAME) === target)
|
|
21297
|
+
return;
|
|
21298
|
+
this._hub.write({ [FILTER_PARAM_NAME]: target }, { replaceUrl: false });
|
|
21279
21299
|
});
|
|
21280
21300
|
}
|
|
21281
|
-
disconnectFilterFromRouter(filterComponent) {
|
|
21282
|
-
if (this._connectedFilter !== filterComponent)
|
|
21283
|
-
return;
|
|
21284
|
-
this._connectedFilter = null;
|
|
21285
|
-
this._activatedRouteSubscription?.unsubscribe();
|
|
21286
|
-
this._filterUrlParameterSubscription?.unsubscribe();
|
|
21287
|
-
}
|
|
21288
21301
|
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
|
|
21302
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService });
|
|
21290
21303
|
}
|
|
21291
21304
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterRouterConnectorService, decorators: [{
|
|
21292
|
-
type: Injectable
|
|
21293
|
-
args: [{
|
|
21294
|
-
providedIn: 'root'
|
|
21295
|
-
}]
|
|
21305
|
+
type: Injectable
|
|
21296
21306
|
}], ctorParameters: () => [] });
|
|
21297
21307
|
|
|
21298
21308
|
class QdDependentFiltersService {
|
|
@@ -21432,7 +21442,7 @@ class QdFilterComponent {
|
|
|
21432
21442
|
_filterDataSubject = new BehaviorSubject(undefined);
|
|
21433
21443
|
_hasPreselectionSubject = new ReplaySubject(1);
|
|
21434
21444
|
_categoriesSubject = new ReplaySubject(1);
|
|
21435
|
-
_filterConnectedWithRouterSubject = new ReplaySubject();
|
|
21445
|
+
_filterConnectedWithRouterSubject = new ReplaySubject(1);
|
|
21436
21446
|
_filterConnectedWithRouter$ = this._filterConnectedWithRouterSubject.asObservable();
|
|
21437
21447
|
set hasPreselection(hasPreselection) {
|
|
21438
21448
|
this._hasPreselectionSubject.next(hasPreselection);
|
|
@@ -21470,7 +21480,6 @@ class QdFilterComponent {
|
|
|
21470
21480
|
}
|
|
21471
21481
|
}
|
|
21472
21482
|
ngOnDestroy() {
|
|
21473
|
-
this.filterRouterConnectorService.disconnectFilterFromRouter(this);
|
|
21474
21483
|
this.dependentFiltersService.destroy();
|
|
21475
21484
|
this._queryStringSubscription?.unsubscribe();
|
|
21476
21485
|
this._postBodySubscription?.unsubscribe();
|
|
@@ -21605,11 +21614,11 @@ class QdFilterComponent {
|
|
|
21605
21614
|
});
|
|
21606
21615
|
}
|
|
21607
21616
|
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 });
|
|
21617
|
+
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
21618
|
}
|
|
21610
21619
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFilterComponent, decorators: [{
|
|
21611
21620
|
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"] }]
|
|
21621
|
+
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
21622
|
}], propDecorators: { filterData: [{
|
|
21614
21623
|
type: Input
|
|
21615
21624
|
}], testId: [{
|
|
@@ -22277,94 +22286,88 @@ function searchReducer(state, action) {
|
|
|
22277
22286
|
}
|
|
22278
22287
|
|
|
22279
22288
|
const PHRASE_PRESELECT_SEPARATOR = '|';
|
|
22289
|
+
const SEARCH_PARAM_NAME = 'search';
|
|
22290
|
+
const OWNED_PARAMS$3 = [SEARCH_PARAM_NAME];
|
|
22291
|
+
const FEATURE_LABEL$3 = { name: 'Search', plural: 'searches' };
|
|
22292
|
+
/**
|
|
22293
|
+
* Per-view adapter that syncs `QdSearchComponent` phrase and preselection with the URL
|
|
22294
|
+
* `?search=` query param via `QdRouterQueryParamHubService`. Reads/parses on activation,
|
|
22295
|
+
* writes when the search service emits a submit. Coordination, ownership, and write
|
|
22296
|
+
* batching are delegated to the hub — see its JSDoc for details. Behavior contracts live
|
|
22297
|
+
* in `search-router-connector.service.spec.ts` and
|
|
22298
|
+
* `router-query-param-hub.integration.cy.ts`.
|
|
22299
|
+
*/
|
|
22280
22300
|
class QdSearchRouterConnectorService {
|
|
22281
|
-
|
|
22282
|
-
|
|
22301
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
22302
|
+
_destroyRef = inject(DestroyRef);
|
|
22283
22303
|
_connectedSearch;
|
|
22284
|
-
|
|
22285
|
-
|
|
22286
|
-
_navigationEnded$;
|
|
22304
|
+
_readSubscription;
|
|
22305
|
+
_writeSubscription;
|
|
22287
22306
|
_activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
22288
22307
|
_activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
22289
22308
|
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
|
-
});
|
|
22309
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
22308
22310
|
}
|
|
22309
22311
|
connectSearchWithRouter(searchComponent) {
|
|
22310
|
-
if (searchComponent.configData?.connectWithRouter !== true || !this.
|
|
22312
|
+
if (searchComponent.configData?.connectWithRouter !== true || !this._hub.isAvailable())
|
|
22311
22313
|
return of(false);
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
'Please set connectWithRouter to false except for one search.');
|
|
22314
|
+
if (this._connectedSearch === searchComponent)
|
|
22315
|
+
return this._activatedRouteChecked$;
|
|
22316
|
+
if (!this._hub.claim(OWNED_PARAMS$3, this, this._destroyRef, FEATURE_LABEL$3))
|
|
22316
22317
|
return of(false);
|
|
22317
|
-
}
|
|
22318
22318
|
this._connectedSearch = searchComponent;
|
|
22319
|
-
this.
|
|
22320
|
-
this.
|
|
22319
|
+
this._activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
22320
|
+
this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
22321
|
+
this.subscribeToUrlChanges();
|
|
22322
|
+
this.subscribeToSearchChanges();
|
|
22321
22323
|
return this._activatedRouteChecked$;
|
|
22322
22324
|
}
|
|
22323
|
-
|
|
22324
|
-
this.
|
|
22325
|
-
|
|
22326
|
-
|
|
22327
|
-
|
|
22328
|
-
|
|
22329
|
-
|
|
22330
|
-
|
|
22331
|
-
|
|
22332
|
-
|
|
22325
|
+
tearDownConnection() {
|
|
22326
|
+
this._connectedSearch = undefined;
|
|
22327
|
+
this._readSubscription?.unsubscribe();
|
|
22328
|
+
this._writeSubscription?.unsubscribe();
|
|
22329
|
+
}
|
|
22330
|
+
subscribeToUrlChanges() {
|
|
22331
|
+
let isFirstEmit = true;
|
|
22332
|
+
this._readSubscription = this._hub.navigationSettled$
|
|
22333
|
+
.pipe(switchMap(() => this._hub.queryParams()), map(queryParams => queryParams[SEARCH_PARAM_NAME]), distinctUntilChanged(), takeUntilDestroyed(this._destroyRef))
|
|
22334
|
+
.subscribe(rawSearch => {
|
|
22335
|
+
if (typeof rawSearch === 'string' && this._connectedSearch) {
|
|
22336
|
+
const [phrase, preSelect] = rawSearch
|
|
22337
|
+
.split(this.getNotEscapedSplitRegExp())
|
|
22338
|
+
.map(value => this.unescape(value));
|
|
22339
|
+
this._connectedSearch.search = phrase;
|
|
22340
|
+
this._connectedSearch.preSelect = preSelect || '';
|
|
22341
|
+
}
|
|
22342
|
+
else if (!isFirstEmit && this._connectedSearch) {
|
|
22343
|
+
this._connectedSearch.search = '';
|
|
22344
|
+
this._connectedSearch.preSelect = '';
|
|
22345
|
+
}
|
|
22346
|
+
if (isFirstEmit) {
|
|
22347
|
+
this._activatedRouteCheckedSubject.next(true);
|
|
22348
|
+
isFirstEmit = false;
|
|
22333
22349
|
}
|
|
22334
|
-
|
|
22350
|
+
});
|
|
22351
|
+
}
|
|
22352
|
+
subscribeToSearchChanges() {
|
|
22353
|
+
this._writeSubscription = this._connectedSearch.searchService.searchPostBody$.pipe(switchMap(searchData => this._hub.navigationSettled$.pipe(take(1), map(() => searchData))), takeUntilDestroyed(this._destroyRef)).subscribe(({ phrase, preSelect }) => {
|
|
22354
|
+
const serialized = this.escape(phrase) + (preSelect ? PHRASE_PRESELECT_SEPARATOR + this.escape(preSelect) : '');
|
|
22355
|
+
const target = serialized || undefined;
|
|
22356
|
+
if (this._hub.snapshotQueryParam(SEARCH_PARAM_NAME) === target)
|
|
22357
|
+
return;
|
|
22358
|
+
this._hub.write({ [SEARCH_PARAM_NAME]: target }, { replaceUrl: false });
|
|
22335
22359
|
});
|
|
22336
22360
|
}
|
|
22337
22361
|
getNotEscapedSplitRegExp() {
|
|
22338
22362
|
return new RegExp('(?<!\\\\)\\' + PHRASE_PRESELECT_SEPARATOR);
|
|
22339
22363
|
}
|
|
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)
|
|
22364
|
+
escape(value) {
|
|
22365
|
+
if (!value)
|
|
22356
22366
|
return '';
|
|
22357
|
-
return
|
|
22358
|
-
}
|
|
22359
|
-
unescape(escapedString) {
|
|
22360
|
-
return escapedString.replace(new RegExp(`\\\\(${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '$1');
|
|
22367
|
+
return value.replace(new RegExp(`(\\${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '\\$1');
|
|
22361
22368
|
}
|
|
22362
|
-
|
|
22363
|
-
|
|
22364
|
-
return;
|
|
22365
|
-
this._connectedSearch = null;
|
|
22366
|
-
this._activatedRouteSubscription?.unsubscribe();
|
|
22367
|
-
this._searchUrlParameterSubscription?.unsubscribe();
|
|
22369
|
+
unescape(escapedValue) {
|
|
22370
|
+
return escapedValue.replace(new RegExp(`\\\\(${PHRASE_PRESELECT_SEPARATOR})`, 'g'), '$1');
|
|
22368
22371
|
}
|
|
22369
22372
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdSearchRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
22370
22373
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdSearchRouterConnectorService });
|
|
@@ -22561,7 +22564,7 @@ class QdSearchComponent {
|
|
|
22561
22564
|
_search = '';
|
|
22562
22565
|
_preSelect = '';
|
|
22563
22566
|
_destroyed$ = new Subject();
|
|
22564
|
-
_searchConnectedWithRouterSubject = new ReplaySubject();
|
|
22567
|
+
_searchConnectedWithRouterSubject = new ReplaySubject(1);
|
|
22565
22568
|
_searchConnectedWithRouter$ = this._searchConnectedWithRouterSubject.asObservable();
|
|
22566
22569
|
get inputConfig() {
|
|
22567
22570
|
return {
|
|
@@ -22644,7 +22647,6 @@ class QdSearchComponent {
|
|
|
22644
22647
|
}
|
|
22645
22648
|
}
|
|
22646
22649
|
ngOnDestroy() {
|
|
22647
|
-
this.searchRouterConnectorService.disconnectSearchFromRouter(this);
|
|
22648
22650
|
this._destroyed$.next();
|
|
22649
22651
|
this._destroyed$.complete();
|
|
22650
22652
|
}
|
|
@@ -23655,11 +23657,26 @@ var QdPaginatorDirection;
|
|
|
23655
23657
|
|
|
23656
23658
|
const PAGE_PARAM_NAME = 'page';
|
|
23657
23659
|
const SIZE_PARAM_NAME = 'size';
|
|
23658
|
-
const OWNED_PARAMS$
|
|
23660
|
+
const OWNED_PARAMS$2 = [PAGE_PARAM_NAME, SIZE_PARAM_NAME];
|
|
23661
|
+
const FEATURE_LABEL$2 = { name: 'Table', plural: 'tables' };
|
|
23659
23662
|
const SIZE_SANITY_MAX = 1000;
|
|
23663
|
+
/**
|
|
23664
|
+
* Per-view adapter that syncs `QdTablePaginatorComponent` page and size with the URL
|
|
23665
|
+
* `?page=` and `?size=` query params via `QdRouterQueryParamHubService`. Applies the URL
|
|
23666
|
+
* synchronously on connect (so cross-adapter store reducers cannot stomp on the deep-linked
|
|
23667
|
+
* page), then keeps store and URL in sync. Coordination, ownership, and write batching are
|
|
23668
|
+
* delegated to the hub — see its JSDoc for details. Behavior contracts live in
|
|
23669
|
+
* `pagination-router-connector.service.spec.ts` and `router-query-param-hub.integration.cy.ts`.
|
|
23670
|
+
*
|
|
23671
|
+
* Internal invariant: relies on `pageChangeInfo$()` and `totalCount$()` being
|
|
23672
|
+
* BehaviorSubject-backed selectors that emit their current value on subscribe. If either
|
|
23673
|
+
* becomes async (delay/debounce), the synchronous snapshot in `applyInitialUrlSynchronously`
|
|
23674
|
+
* stays at its initial default silently — the dispatch would always fire and the table would
|
|
23675
|
+
* flicker to "0 elements" on disconnect/reconnect cycles.
|
|
23676
|
+
*/
|
|
23660
23677
|
class QdTablePaginationRouterConnectorService {
|
|
23661
|
-
|
|
23662
|
-
|
|
23678
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
23679
|
+
_destroyRef = inject(DestroyRef);
|
|
23663
23680
|
_connection;
|
|
23664
23681
|
_readSubscription;
|
|
23665
23682
|
_writeSubscription;
|
|
@@ -23667,15 +23684,12 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23667
23684
|
_activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
23668
23685
|
_hasInitialUrlBeenSet = false;
|
|
23669
23686
|
constructor() {
|
|
23670
|
-
this.
|
|
23671
|
-
this.tearDownConnection();
|
|
23672
|
-
this.hub.release(OWNED_PARAMS$1, this);
|
|
23673
|
-
});
|
|
23687
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
23674
23688
|
}
|
|
23675
23689
|
connectPaginationWithRouter(paginator, tableStoreService) {
|
|
23676
|
-
if (!paginator.shouldConnectWithRouter() || !this.
|
|
23690
|
+
if (!paginator.shouldConnectWithRouter() || !this._hub.isAvailable())
|
|
23677
23691
|
return of(false);
|
|
23678
|
-
if (!this.
|
|
23692
|
+
if (!this._hub.claim(OWNED_PARAMS$2, this, this._destroyRef, FEATURE_LABEL$2))
|
|
23679
23693
|
return of(false);
|
|
23680
23694
|
this._connection = {
|
|
23681
23695
|
paginator: paginator,
|
|
@@ -23684,15 +23698,32 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23684
23698
|
this._hasInitialUrlBeenSet = false;
|
|
23685
23699
|
this._activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
23686
23700
|
this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
23701
|
+
this.applyInitialUrlSynchronously();
|
|
23687
23702
|
this.subscribeToUrlChanges();
|
|
23688
23703
|
this.subscribeToStoreChanges();
|
|
23689
23704
|
return this._activatedRouteChecked$;
|
|
23690
23705
|
}
|
|
23691
|
-
|
|
23692
|
-
|
|
23706
|
+
applyInitialUrlSynchronously() {
|
|
23707
|
+
const { tableStoreService, paginator } = this._connection;
|
|
23708
|
+
const params = this.parseUrlParams({
|
|
23709
|
+
[PAGE_PARAM_NAME]: this._hub.snapshotQueryParam(PAGE_PARAM_NAME),
|
|
23710
|
+
[SIZE_PARAM_NAME]: this._hub.snapshotQueryParam(SIZE_PARAM_NAME)
|
|
23711
|
+
});
|
|
23712
|
+
const pageSize = params.size ?? paginator.getPageSizeDefault();
|
|
23713
|
+
const pageIndex = params.page !== undefined ? params.page - 1 : 0;
|
|
23714
|
+
let currentInfo;
|
|
23715
|
+
let currentTotalCount = 0;
|
|
23716
|
+
tableStoreService
|
|
23717
|
+
.pageChangeInfo$()
|
|
23718
|
+
.pipe(take(1))
|
|
23719
|
+
.subscribe(info => (currentInfo = info));
|
|
23720
|
+
tableStoreService
|
|
23721
|
+
.totalCount$()
|
|
23722
|
+
.pipe(take(1))
|
|
23723
|
+
.subscribe(value => (currentTotalCount = value));
|
|
23724
|
+
if (currentInfo && currentInfo.pageIndex === pageIndex && currentInfo.pageSize === pageSize)
|
|
23693
23725
|
return;
|
|
23694
|
-
|
|
23695
|
-
this.hub.release(OWNED_PARAMS$1, this);
|
|
23726
|
+
tableStoreService.setPageParams(pageIndex, pageSize, currentTotalCount);
|
|
23696
23727
|
}
|
|
23697
23728
|
tearDownConnection() {
|
|
23698
23729
|
this._connection = undefined;
|
|
@@ -23703,8 +23734,8 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23703
23734
|
subscribeToUrlChanges() {
|
|
23704
23735
|
const { tableStoreService } = this._connection;
|
|
23705
23736
|
let isFirstEmit = true;
|
|
23706
|
-
this._readSubscription = this.
|
|
23707
|
-
.pipe(switchMap(() => this.
|
|
23737
|
+
this._readSubscription = this._hub.navigationSettled$
|
|
23738
|
+
.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
23739
|
.subscribe(({ params, currentInfo, totalCount }) => {
|
|
23709
23740
|
const target = this.toStoreState(params);
|
|
23710
23741
|
const isChanged = !currentInfo || currentInfo.pageIndex !== target.pageIndex || currentInfo.pageSize !== target.pageSize;
|
|
@@ -23721,7 +23752,7 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23721
23752
|
const { tableStoreService } = this._connection;
|
|
23722
23753
|
this._writeSubscription = tableStoreService
|
|
23723
23754
|
.pageChangeInfo$()
|
|
23724
|
-
.pipe(filter((info) => info !== undefined), distinctUntilChanged((a, b) => a.pageIndex === b.pageIndex && a.pageSize === b.pageSize), switchMap(info => this.
|
|
23755
|
+
.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
23756
|
.subscribe(info => this.writeUrl(info.pageIndex, info.pageSize));
|
|
23726
23757
|
}
|
|
23727
23758
|
writeUrl(pageIndex, pageSize) {
|
|
@@ -23729,11 +23760,11 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23729
23760
|
this._hasInitialUrlBeenSet = true;
|
|
23730
23761
|
const targetPage = String(pageIndex + 1);
|
|
23731
23762
|
const targetSize = String(pageSize);
|
|
23732
|
-
const urlAlreadyMatches = this.
|
|
23733
|
-
this.
|
|
23763
|
+
const urlAlreadyMatches = this._hub.snapshotQueryParam(PAGE_PARAM_NAME) === targetPage &&
|
|
23764
|
+
this._hub.snapshotQueryParam(SIZE_PARAM_NAME) === targetSize;
|
|
23734
23765
|
if (urlAlreadyMatches)
|
|
23735
23766
|
return;
|
|
23736
|
-
this.
|
|
23767
|
+
this._hub.write({ [PAGE_PARAM_NAME]: targetPage, [SIZE_PARAM_NAME]: targetSize }, { replaceUrl });
|
|
23737
23768
|
}
|
|
23738
23769
|
parseUrlParams(queryParams) {
|
|
23739
23770
|
const result = {};
|
|
@@ -23754,9 +23785,8 @@ class QdTablePaginationRouterConnectorService {
|
|
|
23754
23785
|
}
|
|
23755
23786
|
isValidSize(value) {
|
|
23756
23787
|
const pageSizes = this.getConfiguredPageSizes();
|
|
23757
|
-
if (pageSizes && pageSizes.length > 0)
|
|
23788
|
+
if (pageSizes && pageSizes.length > 0)
|
|
23758
23789
|
return pageSizes.includes(value);
|
|
23759
|
-
}
|
|
23760
23790
|
return value <= SIZE_SANITY_MAX;
|
|
23761
23791
|
}
|
|
23762
23792
|
getConfiguredPageSizes() {
|
|
@@ -24045,7 +24075,6 @@ class QdTablePaginatorComponent {
|
|
|
24045
24075
|
this.startPagination();
|
|
24046
24076
|
}
|
|
24047
24077
|
ngOnDestroy() {
|
|
24048
|
-
this.routerConnector.disconnectPaginationFromRouter(this);
|
|
24049
24078
|
this._destroyed$.next(null);
|
|
24050
24079
|
this._destroyed$.complete();
|
|
24051
24080
|
}
|
|
@@ -24145,12 +24174,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
24145
24174
|
}] } });
|
|
24146
24175
|
|
|
24147
24176
|
const SORT_PARAM_NAME = 'sort';
|
|
24148
|
-
const OWNED_PARAMS = [SORT_PARAM_NAME];
|
|
24177
|
+
const OWNED_PARAMS$1 = [SORT_PARAM_NAME];
|
|
24178
|
+
const FEATURE_LABEL$1 = { name: 'Table', plural: 'tables' };
|
|
24149
24179
|
const SEGMENT_SEPARATOR = ',';
|
|
24150
24180
|
const COLUMN_DIRECTION_SEPARATOR = '.';
|
|
24181
|
+
/**
|
|
24182
|
+
* Per-view adapter that syncs `QdTableComponent` sort state with the URL `?sort=` query
|
|
24183
|
+
* param via `QdRouterQueryParamHubService`. Reads/parses on activation (single segment
|
|
24184
|
+
* only; multi-segment URLs use the first valid entry and warn), writes when the store's
|
|
24185
|
+
* `tableSort$` selector emits. Coordination, ownership, and write batching are delegated
|
|
24186
|
+
* to the hub — see its JSDoc for details. Behavior contracts live in
|
|
24187
|
+
* `sort-router-connector.service.spec.ts` and `router-query-param-hub.integration.cy.ts`.
|
|
24188
|
+
*
|
|
24189
|
+
* Internal invariant: relies on `tableSort$()` being a BehaviorSubject-backed selector that
|
|
24190
|
+
* emits its current value on subscribe. The URL-read pipeline pairs each segments emission
|
|
24191
|
+
* with the latest sort snapshot via `withLatestFrom`; if the selector ever becomes async
|
|
24192
|
+
* (delay/debounce), `withLatestFrom` drops the outer emission until the snapshot arrives
|
|
24193
|
+
* and URL-driven sort changes are silently lost.
|
|
24194
|
+
*/
|
|
24151
24195
|
class QdTableSortRouterConnectorService {
|
|
24152
|
-
|
|
24153
|
-
|
|
24196
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
24197
|
+
_destroyRef = inject(DestroyRef);
|
|
24154
24198
|
_connection;
|
|
24155
24199
|
_readSubscription;
|
|
24156
24200
|
_writeSubscription;
|
|
@@ -24159,15 +24203,12 @@ class QdTableSortRouterConnectorService {
|
|
|
24159
24203
|
_hasInitialUrlBeenSet = false;
|
|
24160
24204
|
_hasWarnedMultiSegment = false;
|
|
24161
24205
|
constructor() {
|
|
24162
|
-
this.
|
|
24163
|
-
this.tearDownConnection();
|
|
24164
|
-
this.hub.release(OWNED_PARAMS, this);
|
|
24165
|
-
});
|
|
24206
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
24166
24207
|
}
|
|
24167
24208
|
connectSortWithRouter(table, tableStoreService) {
|
|
24168
|
-
if (!this.shouldConnect(table) || !this.
|
|
24209
|
+
if (!this.shouldConnect(table) || !this._hub.isAvailable())
|
|
24169
24210
|
return of(false);
|
|
24170
|
-
if (!this.
|
|
24211
|
+
if (!this._hub.claim(OWNED_PARAMS$1, this, this._destroyRef, FEATURE_LABEL$1))
|
|
24171
24212
|
return of(false);
|
|
24172
24213
|
this._connection = {
|
|
24173
24214
|
table: table,
|
|
@@ -24182,12 +24223,6 @@ class QdTableSortRouterConnectorService {
|
|
|
24182
24223
|
this.subscribeToStoreChanges();
|
|
24183
24224
|
return this._activatedRouteChecked$;
|
|
24184
24225
|
}
|
|
24185
|
-
disconnectSortFromRouter(table) {
|
|
24186
|
-
if (this._connection?.table !== table)
|
|
24187
|
-
return;
|
|
24188
|
-
this.tearDownConnection();
|
|
24189
|
-
this.hub.release(OWNED_PARAMS, this);
|
|
24190
|
-
}
|
|
24191
24226
|
tearDownConnection() {
|
|
24192
24227
|
this._connection = undefined;
|
|
24193
24228
|
this._hasInitialUrlBeenSet = false;
|
|
@@ -24198,12 +24233,14 @@ class QdTableSortRouterConnectorService {
|
|
|
24198
24233
|
subscribeToUrlChanges() {
|
|
24199
24234
|
const { tableStoreService } = this._connection;
|
|
24200
24235
|
let isFirstEmit = true;
|
|
24201
|
-
this._readSubscription = this.
|
|
24202
|
-
.pipe(switchMap(() => this.
|
|
24203
|
-
.subscribe(segments => {
|
|
24236
|
+
this._readSubscription = this._hub.navigationSettled$
|
|
24237
|
+
.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$()))
|
|
24238
|
+
.subscribe(([segments, currentSort]) => {
|
|
24204
24239
|
if (segments.length === 1) {
|
|
24205
24240
|
const [{ column, direction }] = segments;
|
|
24206
|
-
|
|
24241
|
+
if (!this.isSameAsCurrentSort(currentSort, column, direction)) {
|
|
24242
|
+
tableStoreService.setSort(column, direction);
|
|
24243
|
+
}
|
|
24207
24244
|
}
|
|
24208
24245
|
if (isFirstEmit) {
|
|
24209
24246
|
this._activatedRouteCheckedSubject.next(true);
|
|
@@ -24211,19 +24248,25 @@ class QdTableSortRouterConnectorService {
|
|
|
24211
24248
|
}
|
|
24212
24249
|
});
|
|
24213
24250
|
}
|
|
24251
|
+
isSameAsCurrentSort(currentSort, column, direction) {
|
|
24252
|
+
if (!currentSort)
|
|
24253
|
+
return false;
|
|
24254
|
+
const active = currentSort.filter(entry => entry.direction !== QdSortDirection.NONE);
|
|
24255
|
+
return active.length === 1 && active[0].column === column && active[0].direction === direction;
|
|
24256
|
+
}
|
|
24214
24257
|
subscribeToStoreChanges() {
|
|
24215
24258
|
const { tableStoreService } = this._connection;
|
|
24216
24259
|
this._writeSubscription = tableStoreService
|
|
24217
24260
|
.tableSort$()
|
|
24218
|
-
.pipe(filter((sort) => Array.isArray(sort)), map(sort => this.serializeSort(sort)), distinctUntilChanged(), switchMap(serialized => this.
|
|
24261
|
+
.pipe(filter((sort) => Array.isArray(sort)), map(sort => this.serializeSort(sort)), distinctUntilChanged(), switchMap(serialized => this._hub.navigationSettled$.pipe(take(1), map(() => serialized))))
|
|
24219
24262
|
.subscribe(serialized => this.writeUrl(serialized));
|
|
24220
24263
|
}
|
|
24221
24264
|
writeUrl(serialized) {
|
|
24222
24265
|
const replaceUrl = !this._hasInitialUrlBeenSet;
|
|
24223
24266
|
this._hasInitialUrlBeenSet = true;
|
|
24224
|
-
if (this.
|
|
24267
|
+
if (this._hub.snapshotQueryParam(SORT_PARAM_NAME) === serialized)
|
|
24225
24268
|
return;
|
|
24226
|
-
this.
|
|
24269
|
+
this._hub.write({ [SORT_PARAM_NAME]: serialized }, { replaceUrl });
|
|
24227
24270
|
}
|
|
24228
24271
|
shouldConnect(table) {
|
|
24229
24272
|
const sortConfig = table.config.sort;
|
|
@@ -25039,7 +25082,6 @@ class QdTableComponent {
|
|
|
25039
25082
|
}
|
|
25040
25083
|
}
|
|
25041
25084
|
ngOnDestroy() {
|
|
25042
|
-
this.sortRouterConnector.disconnectSortFromRouter(this);
|
|
25043
25085
|
this.tableStoreService.updateTableStateRecentSecondaryAction(undefined);
|
|
25044
25086
|
this.tableStoreService.resetConnectorStates();
|
|
25045
25087
|
this.fillingWidthService.destroy();
|
|
@@ -29174,6 +29216,149 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
29174
29216
|
args: ['data-test-id']
|
|
29175
29217
|
}] } });
|
|
29176
29218
|
|
|
29219
|
+
const TAB_PARAM_NAME = 'tab';
|
|
29220
|
+
const OWNED_PARAMS = [TAB_PARAM_NAME];
|
|
29221
|
+
const FEATURE_LABEL = { name: 'Page Tabs', plural: 'page tabs' };
|
|
29222
|
+
/**
|
|
29223
|
+
* Per-view adapter that syncs `QdPageTabsComponent` selection with the URL `?tab=` query
|
|
29224
|
+
* param via `QdRouterQueryParamHubService`. Reads on activation and selects the matching
|
|
29225
|
+
* tab (with fallback for unknown/disabled names), writes on `selectionChange`. Coordination,
|
|
29226
|
+
* ownership, and write batching are delegated to the hub — see its JSDoc for details.
|
|
29227
|
+
* Behavior contracts live in `page-tabs-router-connector.service.spec.ts` and
|
|
29228
|
+
* `router-query-param-hub.integration.cy.ts`.
|
|
29229
|
+
*
|
|
29230
|
+
* Note: this is the only connector that writes synchronously without gating on
|
|
29231
|
+
* `navigationSettled$`. Required for the deep-link case where `ngAfterViewInit` fires
|
|
29232
|
+
* before the initial NavigationEnd is replayed — gating there would hang the click-driven
|
|
29233
|
+
* write pipeline (see commit 57f0a271a).
|
|
29234
|
+
*
|
|
29235
|
+
* Internal invariant: when the requested tab is unknown or disabled, `selectFallbackTab()`
|
|
29236
|
+
* writes the URL explicitly. CdkStepper does not emit `selectionChange` if the fallback is
|
|
29237
|
+
* already the selected step, so without the explicit write deep-links like `?tab=phantom`
|
|
29238
|
+
* would leave a stale param in the URL.
|
|
29239
|
+
*/
|
|
29240
|
+
class QdPageTabsRouterConnectorService {
|
|
29241
|
+
_hub = inject(QdRouterQueryParamHubService);
|
|
29242
|
+
_destroyRef = inject(DestroyRef);
|
|
29243
|
+
_connectedComponent;
|
|
29244
|
+
_readSubscription;
|
|
29245
|
+
_writeSubscription;
|
|
29246
|
+
_activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
29247
|
+
_activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
29248
|
+
_hasInitialUrlBeenSet = false;
|
|
29249
|
+
constructor() {
|
|
29250
|
+
this._destroyRef.onDestroy(() => this.tearDownConnection());
|
|
29251
|
+
}
|
|
29252
|
+
connectTabsWithRouter(component) {
|
|
29253
|
+
if (component.config?.connectWithRouter !== true || !this._hub.isAvailable())
|
|
29254
|
+
return of(false);
|
|
29255
|
+
if (this._connectedComponent === component)
|
|
29256
|
+
return this._activatedRouteChecked$;
|
|
29257
|
+
if (!this._hub.claim(OWNED_PARAMS, this, this._destroyRef, FEATURE_LABEL))
|
|
29258
|
+
return of(false);
|
|
29259
|
+
this._connectedComponent = component;
|
|
29260
|
+
this._hasInitialUrlBeenSet = false;
|
|
29261
|
+
this._activatedRouteCheckedSubject = new ReplaySubject(1);
|
|
29262
|
+
this._activatedRouteChecked$ = this._activatedRouteCheckedSubject.asObservable();
|
|
29263
|
+
this.subscribeToTabChanges();
|
|
29264
|
+
this.subscribeToUrlChanges();
|
|
29265
|
+
return this._activatedRouteChecked$;
|
|
29266
|
+
}
|
|
29267
|
+
tearDownConnection() {
|
|
29268
|
+
this._connectedComponent = undefined;
|
|
29269
|
+
this._hasInitialUrlBeenSet = false;
|
|
29270
|
+
this._readSubscription?.unsubscribe();
|
|
29271
|
+
this._writeSubscription?.unsubscribe();
|
|
29272
|
+
}
|
|
29273
|
+
subscribeToUrlChanges() {
|
|
29274
|
+
let isFirstEmit = true;
|
|
29275
|
+
this._readSubscription = this._hub.navigationSettled$
|
|
29276
|
+
.pipe(switchMap(() => this._hub.queryParams()), map(queryParams => queryParams[TAB_PARAM_NAME]), distinctUntilChanged(), takeUntilDestroyed(this._destroyRef))
|
|
29277
|
+
.subscribe(rawTabName => {
|
|
29278
|
+
this.applyTabFromUrl(rawTabName);
|
|
29279
|
+
if (isFirstEmit) {
|
|
29280
|
+
this._activatedRouteCheckedSubject.next(true);
|
|
29281
|
+
isFirstEmit = false;
|
|
29282
|
+
}
|
|
29283
|
+
});
|
|
29284
|
+
}
|
|
29285
|
+
subscribeToTabChanges() {
|
|
29286
|
+
const component = this._connectedComponent;
|
|
29287
|
+
this._writeSubscription = component.selectionChange
|
|
29288
|
+
.pipe(map(event => event.selectedStep?.config?.name), takeUntilDestroyed(this._destroyRef))
|
|
29289
|
+
.subscribe(name => this.writeUrl(name));
|
|
29290
|
+
}
|
|
29291
|
+
writeUrl(name) {
|
|
29292
|
+
if (!name) {
|
|
29293
|
+
console.warn('Quadrel Framework | QdPageTabs - "connectWithRouter" is active, however the selected <qd-page-tab> has no "name" attribute.');
|
|
29294
|
+
return;
|
|
29295
|
+
}
|
|
29296
|
+
const replaceUrl = !this._hasInitialUrlBeenSet;
|
|
29297
|
+
this._hasInitialUrlBeenSet = true;
|
|
29298
|
+
if (this._hub.snapshotQueryParam(TAB_PARAM_NAME) === name)
|
|
29299
|
+
return;
|
|
29300
|
+
this._hub.write({ [TAB_PARAM_NAME]: name }, { replaceUrl });
|
|
29301
|
+
}
|
|
29302
|
+
applyTabFromUrl(rawTabName) {
|
|
29303
|
+
const component = this._connectedComponent;
|
|
29304
|
+
if (!component)
|
|
29305
|
+
return;
|
|
29306
|
+
if (typeof rawTabName !== 'string' || rawTabName.length === 0) {
|
|
29307
|
+
this.selectFallbackTab();
|
|
29308
|
+
return;
|
|
29309
|
+
}
|
|
29310
|
+
const matchingTab = this.findTabByName(rawTabName);
|
|
29311
|
+
if (matchingTab && !matchingTab.config?.isDisabled) {
|
|
29312
|
+
matchingTab.select();
|
|
29313
|
+
return;
|
|
29314
|
+
}
|
|
29315
|
+
if (matchingTab) {
|
|
29316
|
+
console.warn('Quadrel Framework | QdPageTabs - Tab "' + rawTabName + '" is disabled and cannot be deep-linked.');
|
|
29317
|
+
}
|
|
29318
|
+
else {
|
|
29319
|
+
console.warn('Quadrel Framework | QdPageTabs - No tab found with name "' + rawTabName + '".');
|
|
29320
|
+
}
|
|
29321
|
+
this.selectFallbackTab();
|
|
29322
|
+
}
|
|
29323
|
+
selectFallbackTab() {
|
|
29324
|
+
const component = this._connectedComponent;
|
|
29325
|
+
if (!component)
|
|
29326
|
+
return;
|
|
29327
|
+
const fallbackIndex = this.findFallbackTabIndex();
|
|
29328
|
+
if (fallbackIndex === undefined)
|
|
29329
|
+
return;
|
|
29330
|
+
const fallback = component.tabs.get(fallbackIndex);
|
|
29331
|
+
if (!fallback)
|
|
29332
|
+
return;
|
|
29333
|
+
fallback.select();
|
|
29334
|
+
if (component.config)
|
|
29335
|
+
component.config.selectedIndex = fallbackIndex;
|
|
29336
|
+
this.writeUrl(fallback.config?.name);
|
|
29337
|
+
}
|
|
29338
|
+
findTabByName(name) {
|
|
29339
|
+
return this._connectedComponent?.tabs.find(tab => tab.config?.name === name);
|
|
29340
|
+
}
|
|
29341
|
+
findFallbackTabIndex() {
|
|
29342
|
+
const component = this._connectedComponent;
|
|
29343
|
+
if (!component)
|
|
29344
|
+
return undefined;
|
|
29345
|
+
const configuredIndex = component.config?.selectedIndex;
|
|
29346
|
+
if (typeof configuredIndex === 'number') {
|
|
29347
|
+
const candidate = component.tabs.get(configuredIndex);
|
|
29348
|
+
if (candidate && !candidate.config.isDisabled)
|
|
29349
|
+
return configuredIndex;
|
|
29350
|
+
}
|
|
29351
|
+
const tabsArray = component.tabs.toArray();
|
|
29352
|
+
const firstNonDisabledIndex = tabsArray.findIndex(tab => !tab.config.isDisabled);
|
|
29353
|
+
return firstNonDisabledIndex >= 0 ? firstNonDisabledIndex : undefined;
|
|
29354
|
+
}
|
|
29355
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsRouterConnectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
29356
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsRouterConnectorService });
|
|
29357
|
+
}
|
|
29358
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsRouterConnectorService, decorators: [{
|
|
29359
|
+
type: Injectable
|
|
29360
|
+
}], ctorParameters: () => [] });
|
|
29361
|
+
|
|
29177
29362
|
/**
|
|
29178
29363
|
* **QdPageTabsComponent** provides a non-linear tabbed navigation system within a **QdPage**.
|
|
29179
29364
|
* It enables switching between different sections while maintaining form validation and controlled navigation.
|
|
@@ -29211,13 +29396,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
29211
29396
|
*
|
|
29212
29397
|
* #### **Submit Button Configuration**
|
|
29213
29398
|
*
|
|
29214
|
-
* The submit button at the bottom of the tab system can be configured via `QdPageTabsConfig`:
|
|
29399
|
+
* The submit button at the bottom of the tab system can be configured via `QdPageTabsConfig.submitButton`:
|
|
29215
29400
|
*
|
|
29216
29401
|
* - **i18n**: The translated label for the submit button.
|
|
29217
29402
|
* - **handler**: A callback function that receives form data upon submission.
|
|
29218
29403
|
* - **isDisabled**: Controls whether the button should be active or not.
|
|
29219
29404
|
* - **isHidden**: Determines whether the button is visible.
|
|
29220
|
-
*
|
|
29405
|
+
*
|
|
29406
|
+
* #### **Router Integration**
|
|
29407
|
+
*
|
|
29408
|
+
* - **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
29409
|
*
|
|
29222
29410
|
* #### **Usage**
|
|
29223
29411
|
*
|
|
@@ -29336,8 +29524,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
29336
29524
|
class QdPageTabsComponent extends CdkStepper {
|
|
29337
29525
|
footerService = inject(QdPageFooterService, { optional: true });
|
|
29338
29526
|
pageStoreService = inject(QdPageStoreService);
|
|
29339
|
-
|
|
29340
|
-
route = inject(ActivatedRoute, { optional: true });
|
|
29527
|
+
routerConnector = inject(QdPageTabsRouterConnectorService);
|
|
29341
29528
|
/**
|
|
29342
29529
|
* Configuration of QdPageTabs.
|
|
29343
29530
|
*/
|
|
@@ -29396,8 +29583,11 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29396
29583
|
ngAfterViewInit() {
|
|
29397
29584
|
super.ngAfterViewInit();
|
|
29398
29585
|
setTimeout(() => {
|
|
29586
|
+
if (this.config?.connectWithRouter) {
|
|
29587
|
+
this.configureBookmarkableTabs();
|
|
29588
|
+
return;
|
|
29589
|
+
}
|
|
29399
29590
|
this.initializeTabSelection();
|
|
29400
|
-
this.configureBookmarkableTabs();
|
|
29401
29591
|
});
|
|
29402
29592
|
}
|
|
29403
29593
|
initializeTabSelection() {
|
|
@@ -29408,54 +29598,7 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29408
29598
|
this.selectFirstNotDisabledTab();
|
|
29409
29599
|
}
|
|
29410
29600
|
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();
|
|
29601
|
+
this.routerConnector.connectTabsWithRouter(this).pipe(takeUntilDestroyed(this.destroyRef)).subscribe();
|
|
29459
29602
|
}
|
|
29460
29603
|
isTabSelectable(index) {
|
|
29461
29604
|
const tab = this.tabs.get(index);
|
|
@@ -29463,22 +29606,14 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29463
29606
|
}
|
|
29464
29607
|
/**
|
|
29465
29608
|
* Selects the first tab that is not disabled.
|
|
29466
|
-
* @param updateUrl if true, the URL will be updated to reflect the selected tab.
|
|
29467
29609
|
*/
|
|
29468
|
-
selectFirstNotDisabledTab(
|
|
29610
|
+
selectFirstNotDisabledTab() {
|
|
29469
29611
|
this.tabs.some((tab, tabIndex) => {
|
|
29470
29612
|
if (!tab.config.isDisabled) {
|
|
29471
29613
|
if (this.config) {
|
|
29472
29614
|
this.config.selectedIndex = tabIndex;
|
|
29473
29615
|
}
|
|
29474
29616
|
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
29617
|
return true;
|
|
29483
29618
|
}
|
|
29484
29619
|
else {
|
|
@@ -29495,13 +29630,6 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29495
29630
|
if (tab.config?.isDisabled)
|
|
29496
29631
|
return;
|
|
29497
29632
|
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
29633
|
}
|
|
29506
29634
|
isSubmitButtonShown() {
|
|
29507
29635
|
return this.config?.submitButton?.isHidden !== true && !this.footerService;
|
|
@@ -29526,11 +29654,11 @@ class QdPageTabsComponent extends CdkStepper {
|
|
|
29526
29654
|
});
|
|
29527
29655
|
}
|
|
29528
29656
|
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 });
|
|
29657
|
+
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
29658
|
}
|
|
29531
29659
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageTabsComponent, decorators: [{
|
|
29532
29660
|
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"] }]
|
|
29661
|
+
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
29662
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
29535
29663
|
type: Input
|
|
29536
29664
|
}], testId: [{
|
|
@@ -31925,7 +32053,7 @@ class QdShellHeaderComponent {
|
|
|
31925
32053
|
const navigationService = this.navigationService;
|
|
31926
32054
|
const shellLeftService = this.shellLeftService;
|
|
31927
32055
|
const shellRightService = this.shellRightService;
|
|
31928
|
-
this.backLinkDisplayed$ = navigationService.getPreviousHref$().pipe(withLatestFrom(navigationService.getRouteComponentInstance$()), switchMap(([previousHref, routeComponentInstance]) => {
|
|
32056
|
+
this.backLinkDisplayed$ = navigationService.getPreviousHref$().pipe(withLatestFrom$1(navigationService.getRouteComponentInstance$()), switchMap(([previousHref, routeComponentInstance]) => {
|
|
31929
32057
|
if (typeof previousHref === 'function')
|
|
31930
32058
|
previousHref = previousHref(routeComponentInstance);
|
|
31931
32059
|
return isObservable(previousHref) ? previousHref.pipe(take(1)) : of(previousHref);
|