@acorex-platform/framework-client 2.0.0-next.24 → 2.0.0-next.26

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.
Files changed (26) hide show
  1. package/fesm2022/{acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-Ca3hnWpz.mjs → acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BTLgOdQN.mjs} +243 -29
  2. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BTLgOdQN.mjs.map +1 -0
  3. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-D70kS0q4.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-DH2Wq7ux.mjs} +2 -2
  4. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-D70kS0q4.mjs.map → acorex-platform-framework-client-layout-entity-attachments-page.component-DH2Wq7ux.mjs.map} +1 -1
  5. package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-2uNcMFTM.mjs → acorex-platform-framework-client-layout-entity-bulk-entity.command-DMgR7yq0.mjs} +2 -2
  6. package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-2uNcMFTM.mjs.map → acorex-platform-framework-client-layout-entity-bulk-entity.command-DMgR7yq0.mjs.map} +1 -1
  7. package/fesm2022/{acorex-platform-framework-client-layout-entity-file-list-popup.component-Cj27Eok3.mjs → acorex-platform-framework-client-layout-entity-file-list-popup.component-DLt5p2Iy.mjs} +2 -2
  8. package/fesm2022/{acorex-platform-framework-client-layout-entity-file-list-popup.component-Cj27Eok3.mjs.map → acorex-platform-framework-client-layout-entity-file-list-popup.component-DLt5p2Iy.mjs.map} +1 -1
  9. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-CNyKkM7m.mjs → acorex-platform-framework-client-layout-entity-gallery-page.component-C_JIijQZ.mjs} +2 -2
  10. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-CNyKkM7m.mjs.map → acorex-platform-framework-client-layout-entity-gallery-page.component-C_JIijQZ.mjs.map} +1 -1
  11. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +1 -1
  12. package/fesm2022/{acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-CELbt6ig.mjs → acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-DRh91YCL.mjs} +26 -5
  13. package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-DRh91YCL.mjs.map +1 -0
  14. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-BSjcMKNA.mjs → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-Ddx_b04d.mjs} +2 -2
  15. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-BSjcMKNA.mjs.map → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-Ddx_b04d.mjs.map} +1 -1
  16. package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-Co5_b33I.mjs → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-DqIuIn76.mjs} +2 -2
  17. package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-Co5_b33I.mjs.map → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-DqIuIn76.mjs.map} +1 -1
  18. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +1 -1
  19. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +168 -81
  20. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
  21. package/package.json +2 -2
  22. package/types/acorex-platform-framework-client-layout-entity.d.ts +57 -2
  23. package/types/acorex-platform-framework-client-layout-widgets.d.ts +2 -0
  24. package/types/acorex-platform-framework-client-themes-default.d.ts +4 -5
  25. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-Ca3hnWpz.mjs.map +0 -1
  26. package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-CELbt6ig.mjs.map +0 -1
@@ -30,13 +30,13 @@ import { AXUnsubscriber } from '@acorex/core/utils';
30
30
  import { AXPAuthModule, AXPSessionService, AXPSessionStatus } from '@acorex-platform/framework-client/auth';
31
31
  import * as i1$3 from '@acorex-platform/framework-client/core';
32
32
  import { AXPEntityBroadcastService, AXPDeviceService, AXHighlightService, AXPPageContextRegistry, AXPPageContextPriority, AXPComponentSlotModule, AXPKeyboardShortcutRegistry, AXPLogoComponent, AXPBroadcastEventService, AXPContextStore } from '@acorex-platform/framework-client/core';
33
- import { getDataSourcePageIndex, applyDataSourcePagingWithoutLoad, AXPEntityCardComponent, AXPEntityCardSkeletonComponent, AXP_CATEGORY_TREE_ROOT_TITLE_I18N_KEY, AXPEntityDefinitionRegistryService, AXPCategoryTreeService, traceEntityMasterList, buildDataSourceTraceSnapshot, AXPEntityListViewColumnViewModel, getEntityInfo, axpRunEntityListPageContextAction, axpBuildEntityListPageContextSnapshot, restoreEntityListExpandedRows, AXP_ENTITY_CONFIG_TOKEN } from '@acorex-platform/framework-client/layout/entity';
33
+ import { getDataSourcePageIndex, applyDataSourcePagingWithoutLoad, watchNextDataSourceLoad, AXPEntityCardComponent, AXPEntityCardSkeletonComponent, AXP_CATEGORY_TREE_ROOT_TITLE_I18N_KEY, AXPEntityDefinitionRegistryService, AXPCategoryTreeService, traceEntityMasterList, buildDataSourceTraceSnapshot, AXPEntityListViewColumnViewModel, getEntityInfo, axpRunEntityListPageContextAction, axpBuildEntityListPageContextSnapshot, restoreEntityListExpandedRows, AXP_ENTITY_CONFIG_TOKEN } from '@acorex-platform/framework-client/layout/entity';
34
34
  import { mergeInfiniteScrollPage, AXPInfiniteScrollDirective, AXPStateMessageComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutToolbarComponent, AXPQueryViewSaveDialogService, AXPEntityListStylingDialogService, AXPQueryFiltersComponent, AXPQuerySortsComponent, AXPQueryViewSaveButtonComponent, AXPQueryViewTabsComponent, AXPQueryColumnsComponent, AXPThemeLayoutStartSideComponent, AXPTaskBadgeService, AXPMenuBadgeHelper, AXPTaskBadgeDirective, provideFileChooserHooks } from '@acorex-platform/framework-client/layout/components';
35
35
  import { resolveEntityCardAvatarPath, listEntityMasterStylingLockedRules, withQueryViewFilterVisibility, queryViewConditionsToFilterQueries } from '@acorex-platform/framework-shared/entity';
36
36
  import * as i1 from '@angular/common';
37
37
  import { CommonModule, AsyncPipe } from '@angular/common';
38
38
  import * as i0 from '@angular/core';
39
- import { input, signal, computed, viewChild, effect, untracked, afterRenderEffect, ChangeDetectionStrategy, Component, inject, ViewChild, Input, ViewEncapsulation, afterNextRender, Injector, output, DestroyRef, ElementRef, NgModule } from '@angular/core';
39
+ import { input, signal, computed, effect, untracked, viewChild, afterRenderEffect, ChangeDetectionStrategy, Component, inject, ViewChild, Input, ViewEncapsulation, afterNextRender, Injector, output, DestroyRef, ElementRef, NgModule } from '@angular/core';
40
40
  import { get, isEqual, sortBy } from 'lodash-es';
41
41
  import * as i7$1 from '@acorex-platform/framework-client/layout/widget-core';
42
42
  import { AXPWidgetCoreModule } from '@acorex-platform/framework-client/layout/widget-core';
@@ -74,6 +74,7 @@ import { AXResizableDirective } from '@acorex/cdk/resizable';
74
74
  const LOAD_MORE_SKELETON_COUNT = 3;
75
75
  //#endregion
76
76
  class AXPEntityMasterListCardListComponent {
77
+ #viewTransitionEffect;
77
78
  //#endregion
78
79
  //#region ----- Lifecycle -----
79
80
  constructor() {
@@ -112,6 +113,19 @@ class AXPEntityMasterListCardListComponent {
112
113
  ...(ngDevMode ? [{ debugName: "showListStatus" }] : /* istanbul ignore next */ []));
113
114
  this.skeletonCount = computed(() => Math.max(this.pageSize(), 1), /* @ts-ignore */
114
115
  ...(ngDevMode ? [{ debugName: "skeletonCount" }] : /* istanbul ignore next */ []));
116
+ this.#viewTransitionEffect = effect(() => {
117
+ const vm = this.vm();
118
+ if (!vm.listTransitionLoading?.()) {
119
+ return;
120
+ }
121
+ untracked(() => {
122
+ this.items.set([]);
123
+ this.hasMore.set(false);
124
+ this.loading.set(true);
125
+ this.loadingMore.set(false);
126
+ });
127
+ }, /* @ts-ignore */
128
+ ...(ngDevMode ? [{ debugName: "#viewTransitionEffect" }] : /* istanbul ignore next */ []));
115
129
  this.loadMoreSkeletonPlaceholders = Array.from({ length: LOAD_MORE_SKELETON_COUNT }, (_, index) => index);
116
130
  this.showSkeletonAvatar = computed(() => {
117
131
  if (this.vm().cardLayout()?.header?.avatar) {
@@ -345,9 +359,14 @@ class AXPEntityMasterListCardListComponent {
345
359
  }
346
360
  async loadPage(reset) {
347
361
  const vm = this.vm();
362
+ const switchEpoch = vm.viewSwitchEpoch?.() ?? null;
363
+ const stillCurrent = () => this.isListSwitchCurrent(vm, switchEpoch);
348
364
  const take = this.resolvePageSize(vm);
349
365
  this.pageSize.set(take);
350
366
  const skip = reset ? 0 : this.items().length;
367
+ if (!stillCurrent()) {
368
+ return;
369
+ }
351
370
  this.appending = !reset;
352
371
  if (reset) {
353
372
  this.items.set([]);
@@ -359,17 +378,41 @@ class AXPEntityMasterListCardListComponent {
359
378
  }
360
379
  applyDataSourcePagingWithoutLoad(vm.dataSource, { take, skip });
361
380
  invalidateDataSourceFetchedPages(vm.dataSource);
381
+ if (!stillCurrent()) {
382
+ this.appending = false;
383
+ return;
384
+ }
385
+ const watch = watchNextDataSourceLoad(vm.dataSource, stillCurrent);
362
386
  try {
363
- const dataReady = this.waitForDataSourceLoad(vm.dataSource);
364
387
  vm.dataSource.refresh({ reset });
365
- await dataReady;
388
+ if (!stillCurrent()) {
389
+ watch.stop();
390
+ return;
391
+ }
392
+ if (vm.dataSource.isLoading) {
393
+ await watch.promise;
394
+ }
395
+ else {
396
+ watch.stop();
397
+ }
398
+ if (!stillCurrent()) {
399
+ return;
400
+ }
366
401
  this.applyPageFromDataSource(reset);
367
402
  }
368
403
  finally {
369
404
  this.appending = false;
370
- this.loading.set(false);
371
- this.loadingMore.set(false);
405
+ if (stillCurrent()) {
406
+ this.loading.set(false);
407
+ this.loadingMore.set(false);
408
+ }
409
+ }
410
+ }
411
+ isListSwitchCurrent(vm, epoch) {
412
+ if (epoch == null || vm.viewSwitchEpoch == null) {
413
+ return true;
372
414
  }
415
+ return vm.viewSwitchEpoch() === epoch;
373
416
  }
374
417
  applyPageFromDataSource(reset) {
375
418
  const ds = this.vm().dataSource;
@@ -386,32 +429,6 @@ class AXPEntityMasterListCardListComponent {
386
429
  resolvePageSize(vm) {
387
430
  return vm.getResolvedListPaging().take || vm.dataSource.config.pageSize || 10;
388
431
  }
389
- waitForDataSourceLoad(dataSource) {
390
- return new Promise((resolve) => {
391
- let settled = false;
392
- const finish = () => {
393
- if (settled) {
394
- return;
395
- }
396
- settled = true;
397
- clearTimeout(timeoutId);
398
- loadingSub.unsubscribe();
399
- changedSub.unsubscribe();
400
- resolve();
401
- };
402
- const changedSub = dataSource.onChanged.subscribe(() => {
403
- if (!dataSource.isLoading) {
404
- queueMicrotask(finish);
405
- }
406
- });
407
- const loadingSub = dataSource.onLoadingChanged.subscribe((isLoading) => {
408
- if (!isLoading) {
409
- queueMicrotask(finish);
410
- }
411
- });
412
- const timeoutId = setTimeout(finish, 10_000);
413
- });
414
- }
415
432
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPEntityMasterListCardListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
416
433
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPEntityMasterListCardListComponent, isStandalone: true, selector: "axp-entity-master-list-card-list", inputs: { vm: { classPropertyName: "vm", publicName: "vm", isSignal: true, isRequired: true, transformFunction: null }, userStylingRules: { classPropertyName: "userStylingRules", publicName: "userStylingRules", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "cardGrid", first: true, predicate: ["cardGrid"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"axp-entity-card-list\" [class.axp-entity-card-list--rows]=\"isRowLayout()\">\n @if (loading() && !items().length) {\n <!-- ----- Skeleton cards while the first page is fetched ----- -->\n <div class=\"axp-entity-card-list__grid\" aria-busy=\"true\" aria-live=\"polite\">\n @for (index of skeletonPlaceholders(); track index) {\n <div class=\"axp-entity-card-list__cell\">\n <axp-entity-card-skeleton\n [fieldCount]=\"skeletonFieldCount()\"\n [badgeCount]=\"skeletonBadgeCount()\"\n [showContent]=\"true\"\n [showFooter]=\"skeletonShowFooter()\"\n [rowLayout]=\"isRowLayout()\"\n [showAvatar]=\"showSkeletonAvatar()\"\n [fillHost]=\"true\"\n />\n </div>\n }\n </div>\n } @else if (!items().length) {\n <!-- ----- Empty state when the list has no rows ----- -->\n <div class=\"axp-entity-card-list__empty\">\n <axp-state-message\n icon=\"fa-light fa-clipboard-list\"\n [title]=\"'@general:empty-states.no-records-found.title'\"\n [description]=\"'@general:empty-states.no-records-found.description'\"\n ></axp-state-message>\n </div>\n } @else {\n <!-- ----- Accumulated card grid with infinite scroll ----- -->\n <div #cardGrid class=\"axp-entity-card-list__grid\">\n @for (row of items(); track trackRow($index, row)) {\n <!--->\n <!-- ----- Render a card when the row record is present ----- -->\n @if (row) {\n <div class=\"axp-entity-card-list__cell\">\n <axp-entity-card\n [entity]=\"vm().entityDef\"\n [layout]=\"vm().cardLayout()\"\n [row]=\"row\"\n [fields]=\"vm().cardFields()\"\n [userStylingRules]=\"userStylingRules()\"\n [variant]=\"isRowLayout() ? 'row' : 'card'\"\n [interactive]=\"true\"\n [fillHost]=\"true\"\n [extraExpanded]=\"isCardExpanded(row)\"\n [showSelection]=\"showSelection()\"\n [selected]=\"isRowSelected(row)\"\n [showNewBadge]=\"isRowHighlighted(row)\"\n [primaryActions]=\"vm().primaryRowActions()\"\n [secondaryActionLoader]=\"loadSecondaryActions\"\n (extraExpandedChange)=\"onCardExtraExpanded($index, $event)\"\n (selectionChange)=\"onRowSelectionChange(row, $event)\"\n (commandClick)=\"onCommandClick($event, row)\"\n />\n </div>\n }\n }\n <!--->\n <!-- ----- Skeleton cards while the next chunk is fetched ----- -->\n @if (loadingMore()) {\n @for (index of loadMoreSkeletonPlaceholders; track index) {\n <div class=\"axp-entity-card-list__cell\">\n <axp-entity-card-skeleton\n [fieldCount]=\"skeletonFieldCount()\"\n [badgeCount]=\"skeletonBadgeCount()\"\n [showContent]=\"true\"\n [showFooter]=\"skeletonShowFooter()\"\n [rowLayout]=\"isRowLayout()\"\n [showAvatar]=\"showSkeletonAvatar()\"\n [fillHost]=\"true\"\n />\n </div>\n }\n }\n <div\n axpInfiniteScroll\n class=\"axp-entity-card-list__sentinel\"\n [axpInfiniteScrollEnabled]=\"canLoadMore()\"\n (axpInfiniteScrollReached)=\"onInfiniteScrollReached()\"\n ></div>\n </div>\n <!--------------------------------------------------------------->\n <!-- ----- Compact loaded count, pinned under the scrolling grid ----- -->\n @if (showListStatus()) {\n <div class=\"axp-entity-card-list__status\" aria-live=\"polite\">\n {{\n '@general:terms.interface.list-range'\n | translate: { params: { loaded: loadedCount(), total: totalCount() } }\n | async\n }}\n </div>\n }\n }\n</div>\n", styles: ["@layer properties;:host.axp-entity-list-host{display:flex;height:100%;min-height:0px;flex-direction:column;overflow:hidden}.axp-entity-card-list{display:flex;min-height:0px;flex:1;flex-direction:column;gap:var(--spacing, .25rem);padding-inline:calc(var(--spacing, .25rem) * 3)}.axp-entity-card-list .axp-entity-card-list__empty{display:flex;min-height:0px;flex:1;align-items:center;justify-content:center;border-radius:var(--radius-lg, .5rem);border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:rgba(var(--ax-sys-color-lightest-surface))}.axp-entity-card-list .axp-entity-card-list__grid{display:grid;min-height:0px;flex:1;grid-template-columns:repeat(1,minmax(0,1fr));align-items:stretch;gap:calc(var(--spacing, .25rem) * 2);overflow:auto;align-content:start;grid-auto-rows:auto}@media(width>=48rem){.axp-entity-card-list .axp-entity-card-list__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(width>=80rem){.axp-entity-card-list .axp-entity-card-list__grid{grid-template-columns:repeat(3,minmax(0,1fr))}}.axp-entity-card-list .axp-entity-card-list__cell{display:flex;min-width:0;flex-direction:column}.axp-entity-card-list .axp-entity-card-list__cell>axp-entity-card,.axp-entity-card-list .axp-entity-card-list__cell>axp-entity-card-skeleton{display:flex;width:100%;min-width:0;flex:1 1 auto;flex-direction:column}.axp-entity-card-list.axp-entity-card-list--rows .axp-entity-card-list__grid{grid-template-columns:repeat(1,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 2)}@media(width>=48rem){.axp-entity-card-list.axp-entity-card-list--rows .axp-entity-card-list__grid{grid-template-columns:repeat(1,minmax(0,1fr))}}@media(width>=80rem){.axp-entity-card-list.axp-entity-card-list--rows .axp-entity-card-list__grid{grid-template-columns:repeat(1,minmax(0,1fr))}}.axp-entity-card-list .axp-entity-card-list__sentinel{grid-column:1 / -1;height:1px;width:100%}.axp-entity-card-list .axp-entity-card-list__status{display:flex;flex-shrink:0;align-items:center;justify-content:flex-end;padding-top:calc(var(--spacing, .25rem) * .5);padding-bottom:var(--spacing, .25rem);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgba(var(--ax-sys-color-text-default))}@supports (color: color-mix(in lab,red,red)){.axp-entity-card-list .axp-entity-card-list__status{color:color-mix(in oklab,rgba(var(--ax-sys-color-text-default)) 80%,transparent)}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXPEntityCardComponent, selector: "axp-entity-card", inputs: ["entity", "layout", "row", "fields", "userStylingRules", "variant", "interactive", "embedded", "elevated", "disableLookupPopover", "fillHost", "extraExpanded", "selected", "showNewBadge", "showSelection", "showClose", "primaryActions", "primaryActionLook", "secondaryActionLoader"], outputs: ["extraExpandedChange", "selectionChange", "commandClick", "closeClick"] }, { kind: "component", type: AXPEntityCardSkeletonComponent, selector: "axp-entity-card-skeleton", inputs: ["fieldCount", "badgeCount", "showFooter", "showHeaderMenu", "showContent", "rowLayout", "showAvatar", "fillHost", "embedded", "elevated"] }, { kind: "directive", type: AXPInfiniteScrollDirective, selector: "[axpInfiniteScroll]", inputs: ["axpInfiniteScrollEnabled", "axpInfiniteScrollRoot", "axpInfiniteScrollRootMargin", "axpInfiniteScrollThreshold"], outputs: ["axpInfiniteScrollReached"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
417
434
  }
@@ -1738,6 +1755,8 @@ class AXPEntityMasterToolbarViewComponent {
1738
1755
  this.translationService = inject(AXTranslationService);
1739
1756
  this.toastService = inject(AXToastService);
1740
1757
  this.isInitializing = false;
1758
+ /** Which `runInitialListLoad` currently owns `isInitializing`. */
1759
+ this.initializingToken = null;
1741
1760
  this.pendingInitialSorts = true;
1742
1761
  this.filterTriggerMode = 'manual';
1743
1762
  this.sortMenuOpen = signal(false, /* @ts-ignore */
@@ -1818,9 +1837,14 @@ class AXPEntityMasterToolbarViewComponent {
1818
1837
  return;
1819
1838
  }
1820
1839
  untracked(() => {
1821
- this.isInitializing = true;
1840
+ const loadOwnsGate = this.initializingToken != null;
1841
+ if (!loadOwnsGate) {
1842
+ this.isInitializing = true;
1843
+ }
1822
1844
  this.setInitialFiltersFromSettingsOrView();
1823
- this.isInitializing = false;
1845
+ if (!loadOwnsGate) {
1846
+ this.isInitializing = false;
1847
+ }
1824
1848
  });
1825
1849
  }, /* @ts-ignore */
1826
1850
  ...(ngDevMode ? [{ debugName: "#listInstanceFiltersEffect" }] : /* istanbul ignore next */ []));
@@ -1870,23 +1894,42 @@ class AXPEntityMasterToolbarViewComponent {
1870
1894
  * Applies persisted view/filters/sorts once after query-filters syncs `initialFilters`.
1871
1895
  * Avoids a second list query from `scheduleApply` plus a follow-up `onFiltersChanged` emit.
1872
1896
  */
1873
- async runInitialListLoad() {
1897
+ async runInitialListLoad(viewSwitchEpoch) {
1898
+ const stillCurrent = () => viewSwitchEpoch == null || this.vm.isViewSwitchCurrent(viewSwitchEpoch);
1899
+ if (!stillCurrent()) {
1900
+ return;
1901
+ }
1902
+ const token = viewSwitchEpoch ?? -1;
1874
1903
  traceEntityMasterList('toolbar', 'runInitialListLoad start', () => ({
1875
1904
  viewName: this.vm.view().name,
1876
1905
  activeListLayout: this.vm.activeListLayout(),
1877
1906
  dataSource: buildDataSourceTraceSnapshot(this.vm.dataSource),
1878
1907
  }));
1879
1908
  this.isInitializing = true;
1909
+ this.initializingToken = token;
1880
1910
  this.pendingInitialSorts = true;
1881
- this.setInitialFiltersFromSettingsOrView();
1882
- this.filtersDefinitions.set(this.vm.filterDefinitions());
1883
- this.filtersReady.set(true);
1884
- await this.waitForQueryFiltersSync();
1885
- await this.vm.applyFilterAndSort();
1886
- this.previousFilterQueries.set(this.vm.filterQueries());
1887
- this.stripFiltersFromRouteQueryParam();
1888
- this.isInitializing = false;
1889
- this.pendingInitialSorts = false;
1911
+ try {
1912
+ this.setInitialFiltersFromSettingsOrView();
1913
+ this.filtersDefinitions.set(this.vm.filterDefinitions());
1914
+ this.filtersReady.set(true);
1915
+ await this.waitForQueryFiltersSync();
1916
+ if (!stillCurrent()) {
1917
+ return;
1918
+ }
1919
+ await this.vm.applyFilterAndSort({
1920
+ resetPagination: viewSwitchEpoch != null,
1921
+ viewSwitchEpoch,
1922
+ forceReload: viewSwitchEpoch != null,
1923
+ });
1924
+ if (!stillCurrent()) {
1925
+ return;
1926
+ }
1927
+ this.previousFilterQueries.set(this.vm.filterQueries());
1928
+ this.stripFiltersFromRouteQueryParam();
1929
+ }
1930
+ finally {
1931
+ this.releaseInitializing(token);
1932
+ }
1890
1933
  traceEntityMasterList('toolbar', 'runInitialListLoad complete', () => ({
1891
1934
  viewName: this.vm.view().name,
1892
1935
  activeListLayout: this.vm.activeListLayout(),
@@ -1894,6 +1937,15 @@ class AXPEntityMasterToolbarViewComponent {
1894
1937
  dataSource: buildDataSourceTraceSnapshot(this.vm.dataSource),
1895
1938
  }));
1896
1939
  }
1940
+ /** Drops the init gate only if a newer load has not taken it. */
1941
+ releaseInitializing(token) {
1942
+ if (this.initializingToken !== token) {
1943
+ return;
1944
+ }
1945
+ this.isInitializing = false;
1946
+ this.pendingInitialSorts = false;
1947
+ this.initializingToken = null;
1948
+ }
1897
1949
  /** Waits until query-filters has rendered and synced converted `initialFilters`. */
1898
1950
  waitForQueryFiltersSync() {
1899
1951
  return new Promise((resolve) => {
@@ -1950,21 +2002,26 @@ class AXPEntityMasterToolbarViewComponent {
1950
2002
  }, 50);
1951
2003
  }
1952
2004
  async onViewChanged(view) {
2005
+ const switchingView = view.name !== this.vm.view().name;
2006
+ const viewSwitchEpoch = switchingView ? this.vm.beginViewSwitch() : undefined;
1953
2007
  traceEntityMasterList('toolbar', 'view tab changed', () => ({
1954
2008
  previousViewName: this.vm.view().name,
1955
2009
  nextViewName: view.name,
1956
2010
  activeListLayout: this.vm.activeListLayout(),
1957
2011
  dataSource: buildDataSourceTraceSnapshot(this.vm.dataSource),
1958
2012
  }));
1959
- await this.vm.setView(view.name);
2013
+ await this.vm.setView(view.name, viewSwitchEpoch);
2014
+ if (viewSwitchEpoch != null && !this.vm.isViewSwitchCurrent(viewSwitchEpoch)) {
2015
+ return;
2016
+ }
1960
2017
  if (axpIsEntityListRoute(this.router)) {
1961
- axpReplaceEntityListRouteQueryParams(this.router, { view: view.name });
1962
- // Prevent queryParamSub from re-applying the same view change while initial load runs.
2018
+ // Mark the next URL before replaceUrl so the queryParam emission does not start a second switch.
1963
2019
  this.vm.markRouteQueryContextSynced(this.activeRoute.snapshot.queryParamMap, {
1964
2020
  view: view.name,
1965
2021
  });
2022
+ axpReplaceEntityListRouteQueryParams(this.router, { view: view.name });
1966
2023
  }
1967
- await this.runInitialListLoad();
2024
+ await this.runInitialListLoad(viewSwitchEpoch);
1968
2025
  traceEntityMasterList('toolbar', 'view tab change handled', () => ({
1969
2026
  viewName: this.vm.view().name,
1970
2027
  activeListLayout: this.vm.activeListLayout(),
@@ -2469,6 +2526,12 @@ class AXPEntityMasterListViewComponent extends AXPPageLayoutBaseComponent {
2469
2526
  this.syncListViewQueryParam(currentViewName);
2470
2527
  }
2471
2528
  });
2529
+ effect(() => {
2530
+ if (!this.vm.listTransitionLoading()) {
2531
+ return;
2532
+ }
2533
+ this.showTableLoadingPlaceholders();
2534
+ });
2472
2535
  effect(() => {
2473
2536
  const currentViewName = this.vm.view().name;
2474
2537
  if (!this.listHostReady()) {
@@ -2811,6 +2874,7 @@ class AXPEntityMasterListViewComponent extends AXPPageLayoutBaseComponent {
2811
2874
  });
2812
2875
  }
2813
2876
  async applyVisibleListRefresh(resetPagination, refreshTargetId, highlightRowId) {
2877
+ const epoch = this.vm.viewSwitchEpoch();
2814
2878
  traceEntityMasterList('list-view', 'refresh event received', () => ({
2815
2879
  viewName: this.vm.view().name,
2816
2880
  activeListLayout: this.vm.activeListLayout(),
@@ -2835,12 +2899,16 @@ class AXPEntityMasterListViewComponent extends AXPPageLayoutBaseComponent {
2835
2899
  else {
2836
2900
  await this.reloadListData(resetPagination);
2837
2901
  }
2902
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2903
+ return;
2904
+ }
2838
2905
  if (typeof highlightRowId === 'string' || typeof highlightRowId === 'number') {
2839
2906
  await this.highlightRowById(highlightRowId);
2840
2907
  }
2841
2908
  traceEntityMasterList('list-view', 'refresh event handled', () => this.buildListHostTraceSnapshot());
2842
2909
  }
2843
2910
  async reloadListData(resetPagination = false) {
2911
+ const epoch = this.vm.viewSwitchEpoch();
2844
2912
  traceEntityMasterList('list-view', 'reloadListData start', () => ({
2845
2913
  resetPagination,
2846
2914
  activeListLayout: this.vm.activeListLayout(),
@@ -2849,12 +2917,22 @@ class AXPEntityMasterListViewComponent extends AXPPageLayoutBaseComponent {
2849
2917
  hasCardList: !!this.cardList(),
2850
2918
  dataSource: buildDataSourceTraceSnapshot(this.vm.dataSource),
2851
2919
  }));
2920
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2921
+ return;
2922
+ }
2852
2923
  if (!resetPagination && !this.vm.isCardHostLayout()) {
2853
2924
  await this.vm.ensureListPagingResolved();
2925
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2926
+ return;
2927
+ }
2854
2928
  this.vm.applyPagingToDataSourceWithoutLoad();
2855
2929
  }
2930
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2931
+ return;
2932
+ }
2856
2933
  if (this.vm.isCardHostLayout()) {
2857
2934
  await this.cardList()?.reload(true);
2935
+ this.vm.finishListTransition(epoch);
2858
2936
  // Shared DS load updates table rows via onChanged but not isRefreshCalled; prepare for a later table switch.
2859
2937
  this.acknowledgeManualTableReady();
2860
2938
  traceEntityMasterList('list-view', 'reloadListData complete (card)', () => this.buildListHostTraceSnapshot());
@@ -2862,21 +2940,59 @@ class AXPEntityMasterListViewComponent extends AXPPageLayoutBaseComponent {
2862
2940
  }
2863
2941
  const grid = this.grid();
2864
2942
  if (grid) {
2865
- const dataReady = this.waitForDataSourceLoad(grid.dataSource);
2943
+ if (resetPagination || this.vm.listTransitionLoading()) {
2944
+ this.showTableLoadingPlaceholders();
2945
+ }
2946
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2947
+ return;
2948
+ }
2949
+ const watch = watchNextDataSourceLoad(grid.dataSource, () => this.vm.isViewSwitchCurrent(epoch));
2866
2950
  grid.refresh({ reset: resetPagination });
2867
- await dataReady;
2951
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2952
+ watch.stop();
2953
+ return;
2954
+ }
2955
+ if (grid.dataSource.isLoading) {
2956
+ await watch.promise;
2957
+ }
2958
+ else {
2959
+ watch.stop();
2960
+ }
2961
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2962
+ return;
2963
+ }
2964
+ this.vm.finishListTransition(epoch);
2868
2965
  if (!resetPagination) {
2869
2966
  this.scheduleSyncGridPagerUi();
2870
2967
  }
2871
2968
  this.scheduleRestoreExpandedRows();
2872
2969
  }
2873
2970
  else {
2874
- const dataReady = this.waitForDataSourceLoad(this.vm.dataSource);
2971
+ const watch = watchNextDataSourceLoad(this.vm.dataSource, () => this.vm.isViewSwitchCurrent(epoch));
2875
2972
  this.vm.dataSource.refresh({ reset: resetPagination });
2876
- await dataReady;
2973
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2974
+ watch.stop();
2975
+ return;
2976
+ }
2977
+ if (this.vm.dataSource.isLoading) {
2978
+ await watch.promise;
2979
+ }
2980
+ else {
2981
+ watch.stop();
2982
+ }
2983
+ if (!this.vm.isViewSwitchCurrent(epoch)) {
2984
+ return;
2985
+ }
2986
+ this.vm.finishListTransition(epoch);
2877
2987
  }
2878
2988
  traceEntityMasterList('list-view', 'reloadListData complete (table)', () => this.buildListHostTraceSnapshot());
2879
2989
  }
2990
+ /** Skeleton rows so an empty previous result does not stay on screen while the next page loads. */
2991
+ showTableLoadingPlaceholders() {
2992
+ const grid = this.grid();
2993
+ const pageSize = this.vm.dataSource.pageSize || 10;
2994
+ grid?.displayedRows?.set(new Array(pageSize));
2995
+ }
2880
2996
  /**
2881
2997
  * Manual-mode table gates the body on `isRefreshCalled` (set only by `grid.refresh()`).
2882
2998
  * Card loads use shared `dataSource.refresh()`, so rows/totalCount update while the gate stays closed.
@@ -2904,35 +3020,6 @@ class AXPEntityMasterListViewComponent extends AXPPageLayoutBaseComponent {
2904
3020
  dataSource: buildDataSourceTraceSnapshot(this.vm.dataSource),
2905
3021
  };
2906
3022
  }
2907
- /**
2908
- * Waits until a `refresh()` load cycle completes (loading started, then finished with data).
2909
- */
2910
- waitForDataSourceLoad(dataSource) {
2911
- return new Promise((resolve) => {
2912
- let settled = false;
2913
- const finish = () => {
2914
- if (settled) {
2915
- return;
2916
- }
2917
- settled = true;
2918
- clearTimeout(timeoutId);
2919
- loadingSub.unsubscribe();
2920
- changedSub.unsubscribe();
2921
- resolve();
2922
- };
2923
- const changedSub = dataSource.onChanged.subscribe(() => {
2924
- if (!dataSource.isLoading) {
2925
- queueMicrotask(finish);
2926
- }
2927
- });
2928
- const loadingSub = dataSource.onLoadingChanged.subscribe((isLoading) => {
2929
- if (!isLoading) {
2930
- queueMicrotask(finish);
2931
- }
2932
- });
2933
- const timeoutId = setTimeout(finish, 10_000);
2934
- });
2935
- }
2936
3023
  grid() {
2937
3024
  return this.dataTable()?.grid();
2938
3025
  }