@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.
package/index.d.ts CHANGED
@@ -861,6 +861,11 @@ interface QdFilterConfigData {
861
861
  * this property has to be set to false except for one filter
862
862
  * because only one filter per page can be connected with the router.
863
863
  *
864
+ * When mounting multiple filters in the same view, set `connectWithRouter: false` on every
865
+ * filter except the one that should own the URL.
866
+ *
867
+ * Note: This is the only router connector that defaults to `true` for historical reasons; all other router connectors default to `false`.
868
+ *
864
869
  * * @default true
865
870
  */
866
871
  connectWithRouter?: boolean;
@@ -1191,12 +1196,40 @@ declare class QdPushEventsService {
1191
1196
  static ɵprov: i0.ɵɵInjectableDeclaration<QdPushEventsService>;
1192
1197
  }
1193
1198
 
1199
+ /**
1200
+ * Display label for an adapter's owning feature, used by the hub to format ownership-conflict
1201
+ * error messages in user-facing terms (no internal service names leak into logs).
1202
+ */
1203
+ interface QdRouterQueryParamFeatureLabel {
1204
+ /**
1205
+ * Title-cased noun phrase identifying the feature in user-facing framework messages,
1206
+ * e.g. `"Page Tabs"`.
1207
+ */
1208
+ readonly name: string;
1209
+ /**
1210
+ * Lowercase plural noun phrase for inline prose in user-facing framework messages,
1211
+ * e.g. `"page tabs"`.
1212
+ */
1213
+ readonly plural: string;
1214
+ }
1215
+ /**
1216
+ * Options for `QdRouterQueryParamHubService.write()`.
1217
+ */
1218
+ interface QdRouterQueryParamWriteOptions {
1219
+ /**
1220
+ * `true` replaces the current history entry; `false` pushes a new entry. When several
1221
+ * `write()` calls in the same microtask disagree, `true` wins (so an initial replace is
1222
+ * preserved when a follow-up write would otherwise push). The escalation is per-batch —
1223
+ * after each flush the pending flag resets to `false`, so writes that arrive in the next
1224
+ * microtask start a fresh batch with their own `replaceUrl` value.
1225
+ */
1226
+ readonly replaceUrl: boolean;
1227
+ }
1194
1228
  /**
1195
1229
  * Framework-wide funnel for syncing application state with the URL `?key=value` query string.
1196
1230
  *
1197
- * Several Quadrel features mirror their state to the URL the table's sort and pagination today,
1198
- * filter, search, and `qd-page-tabs` over time. Without coordination they each call
1199
- * `router.navigate(...)` directly, which produces two failure modes:
1231
+ * Several Quadrel features mirror their state to the URL via per-feature adapters. Without
1232
+ * coordination they each call `router.navigate(...)` directly, which produces two failure modes:
1200
1233
  *
1201
1234
  * 1. **Race**: two navigates issued in the same JS turn cancel each other. Only the last writer
1202
1235
  * wins; the URL silently loses the cancelled feature's params.
@@ -1226,7 +1259,7 @@ declare class QdPushEventsService {
1226
1259
  *
1227
1260
  * connect(state$: Observable<FilterState>, dispatch: (state: FilterState) => void): void {
1228
1261
  * if (!this._hub.isAvailable()) return;
1229
- * if (!this._hub.claim(['filter'], this, this._destroyRef)) return;
1262
+ * if (!this._hub.claim(['filter'], this, this._destroyRef, { name: 'Filter', plural: 'filters' })) return;
1230
1263
  *
1231
1264
  * this._hub
1232
1265
  * .queryParams()
@@ -1235,7 +1268,7 @@ declare class QdPushEventsService {
1235
1268
  *
1236
1269
  * state$
1237
1270
  * .pipe(takeUntilDestroyed(this._destroyRef))
1238
- * .subscribe(state => this._hub.write({ filter: serializeFilter(state) }, false));
1271
+ * .subscribe(state => this._hub.write({ filter: serializeFilter(state) }, { replaceUrl: false }));
1239
1272
  * }
1240
1273
  * }
1241
1274
  * ```
@@ -1263,6 +1296,7 @@ declare class QdRouterQueryParamHubService {
1263
1296
  private readonly _activatedRoute;
1264
1297
  private readonly _destroyRef;
1265
1298
  private readonly _ownership;
1299
+ private readonly _ownerLabels;
1266
1300
  private readonly _destroyClaims;
1267
1301
  private readonly _navigationSettledSubject;
1268
1302
  private _pendingParams;
@@ -1309,11 +1343,16 @@ declare class QdRouterQueryParamHubService {
1309
1343
  * @param owner Stable identity of the caller — usually `this`. Used as the registry key.
1310
1344
  * @param destroyRef Optional. If provided, the hub releases the keys automatically when the
1311
1345
  * `DestroyRef` fires. Pass the adapter's `inject(DestroyRef)` to make leaks impossible.
1346
+ * @param featureLabel Optional. User-facing display label for the adapter's owning feature. The
1347
+ * hub uses it to format ownership-conflict error messages so application developers see the
1348
+ * feature names they configured (e.g. `Filter`, `Page Tabs`, `Table`) instead of internal
1349
+ * adapter class names. When omitted, the hub falls back to a generic `another adapter` phrase.
1312
1350
  * @returns `true` if every requested key is now owned by `owner`. `false` if at least one key
1313
1351
  * was already owned by a different adapter; the registry is left untouched and `destroyRef`
1314
1352
  * is not registered.
1315
1353
  */
1316
- claim(keys: readonly string[], owner: object, destroyRef?: DestroyRef): boolean;
1354
+ claim(keys: readonly string[], owner: object, destroyRef?: DestroyRef, featureLabel?: QdRouterQueryParamFeatureLabel): boolean;
1355
+ private formatOwnershipConflictMessage;
1317
1356
  /**
1318
1357
  * Drops `owner`'s claim on the given keys. Keys not owned by `owner` are left untouched —
1319
1358
  * a foreign release call cannot steal another adapter's ownership.
@@ -1363,13 +1402,10 @@ declare class QdRouterQueryParamHubService {
1363
1402
  *
1364
1403
  * @param params Partial map of query-param keys to their new values. Missing keys are not touched.
1365
1404
  * An empty object short-circuits and is a no-op.
1366
- * @param replaceUrl `true` replaces the current history entry; `false` pushes a new entry.
1367
- * When several `write()` calls in the same microtask disagree, `true` wins (so an initial
1368
- * replace is preserved when a follow-up write would otherwise push). The escalation is
1369
- * per-batch — after each flush the pending flag resets to `false`, so writes that arrive
1370
- * in the next microtask start a fresh batch with their own `replaceUrl` argument.
1405
+ * @param options Write options see `QdRouterQueryParamWriteOptions` for the `replaceUrl`
1406
+ * semantics and per-batch escalation rules.
1371
1407
  */
1372
- write(params: Record<string, string | undefined>, replaceUrl: boolean): void;
1408
+ write(params: Record<string, string | undefined>, options: QdRouterQueryParamWriteOptions): void;
1373
1409
  private replayCurrentRouterStateForLateSubscribers;
1374
1410
  private forwardSettlingRouterEventsToNavigationSettled;
1375
1411
  private registerAutoRelease;
@@ -11137,21 +11173,27 @@ declare class QdFilterService {
11137
11173
  static ɵprov: i0.ɵɵInjectableDeclaration<QdFilterService>;
11138
11174
  }
11139
11175
 
11176
+ /**
11177
+ * Per-view adapter that syncs `QdFilterComponent` selection with the URL `?filter=` query
11178
+ * param via `QdRouterQueryParamHubService`. Reads/parses on activation, writes on selection
11179
+ * change. Coordination, ownership, and write batching are delegated to the hub — see its
11180
+ * JSDoc for details. Behavior contracts live in `filter-router-connector.service.spec.ts`
11181
+ * and `router-query-param-hub.integration.cy.ts`.
11182
+ */
11140
11183
  declare class QdFilterRouterConnectorService {
11141
- private readonly filterService;
11142
- private readonly router;
11143
- private readonly activatedRoute;
11184
+ private readonly _filterService;
11185
+ private readonly _hub;
11186
+ private readonly _destroyRef;
11144
11187
  private _connectedFilter?;
11145
- private _activatedRouteSubscription;
11146
- private _filterUrlParameterSubscription;
11147
- private _navigationEnded$;
11188
+ private _readSubscription?;
11189
+ private _writeSubscription?;
11148
11190
  private _activatedRouteCheckedSubject;
11149
11191
  private _activatedRouteChecked$;
11150
11192
  constructor();
11151
11193
  connectFilterWithRouter(filterComponent: QdFilterComponent): Observable<boolean>;
11152
- private setFilterSelectionFromUrl;
11153
- private setUrlOnFilterSelection;
11154
- disconnectFilterFromRouter(filterComponent: QdFilterComponent): void;
11194
+ private tearDownConnection;
11195
+ private subscribeToUrlChanges;
11196
+ private subscribeToFilterChanges;
11155
11197
  static ɵfac: i0.ɵɵFactoryDeclaration<QdFilterRouterConnectorService, never>;
11156
11198
  static ɵprov: i0.ɵɵInjectableDeclaration<QdFilterRouterConnectorService>;
11157
11199
  }
@@ -12669,13 +12711,16 @@ interface QdTabSelectionEvent {
12669
12711
  *
12670
12712
  * #### **Submit Button Configuration**
12671
12713
  *
12672
- * The submit button at the bottom of the tab system can be configured via `QdPageTabsConfig`:
12714
+ * The submit button at the bottom of the tab system can be configured via `QdPageTabsConfig.submitButton`:
12673
12715
  *
12674
12716
  * - **i18n**: The translated label for the submit button.
12675
12717
  * - **handler**: A callback function that receives form data upon submission.
12676
12718
  * - **isDisabled**: Controls whether the button should be active or not.
12677
12719
  * - **isHidden**: Determines whether the button is visible.
12678
- * - **connectWithRouter**: If set to true, the tab will be connected to the URL and will be bookmarked. It is obligatory to set the `name` attribute for each tab.
12720
+ *
12721
+ * #### **Router Integration**
12722
+ *
12723
+ * - **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`.
12679
12724
  *
12680
12725
  * #### **Usage**
12681
12726
  *
@@ -12791,11 +12836,10 @@ interface QdTabSelectionEvent {
12791
12836
  * - If an invalid tab name is provided in URL, the first available tab is selected
12792
12837
  * - If no tab parameter is present, the `selectedIndex` or first non-disabled tab is selected
12793
12838
  */
12794
- declare class QdPageTabsComponent extends CdkStepper implements OnInit, OnChanges, AfterContentInit, AfterViewInit, OnDestroy {
12839
+ declare class QdPageTabsComponent extends CdkStepper implements OnInit, OnChanges, AfterContentInit, AfterViewInit {
12795
12840
  readonly footerService: QdPageFooterService;
12796
12841
  private pageStoreService;
12797
- private readonly router;
12798
- private readonly route;
12842
+ private readonly routerConnector;
12799
12843
  /**
12800
12844
  * Configuration of QdPageTabs.
12801
12845
  */
@@ -12818,22 +12862,9 @@ declare class QdPageTabsComponent extends CdkStepper implements OnInit, OnChange
12818
12862
  ngAfterViewInit(): void;
12819
12863
  private initializeTabSelection;
12820
12864
  private configureBookmarkableTabs;
12821
- /**
12822
- * Initializes the tab selection based on the URL parameter 'tab'.
12823
- * @private
12824
- */
12825
- private initializeTabFromUrl;
12826
- /**
12827
- * If the user navigates to a page with a tab selected, the tab will be selected automatically in {@link initializeTabFromUrl()} method.
12828
- * If the user navigates to a page without a tab selected, the first active tab will be selected.
12829
- * @private
12830
- */
12831
- private initializeFirstTabSelection;
12832
- ngOnDestroy(): void;
12833
12865
  private isTabSelectable;
12834
12866
  /**
12835
12867
  * Selects the first tab that is not disabled.
12836
- * @param updateUrl if true, the URL will be updated to reflect the selected tab.
12837
12868
  */
12838
12869
  private selectFirstNotDisabledTab;
12839
12870
  save(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quadrel-enterprise-ui/framework",
3
- "version": "20.12.0",
3
+ "version": "20.13.0",
4
4
  "exports": {
5
5
  "./jest-preset": "./jest-preset.js",
6
6
  "./package.json": {