diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts index 30d1cf9796..8d485251cf 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts @@ -1,4 +1,5 @@ import { + afterNextRender, AfterViewInit, ChangeDetectionStrategy, Component, @@ -186,6 +187,10 @@ export class ExtensibleTableComponent implements AfterViewInit, OnDestr }, ); + private horizontalScrollOffset = 0; + + private hasPendingHorizontalScrollOffset = false; + hasAtLeastOnePermittedAction: boolean; readonly propList: EntityPropList; @@ -232,6 +237,7 @@ export class ExtensibleTableComponent implements AfterViewInit, OnDestr } this._data.set(dataValue.map((record, index) => this.prepareRecord(record, index))); + this.restoreHorizontalScrollOffset(); }); } @@ -365,11 +371,55 @@ export class ExtensibleTableComponent implements AfterViewInit, OnDestr this.list() ?.requestStatus$?.pipe(filter(status => status === 'loading')) .subscribe(() => { + this.rememberHorizontalScrollOffset(); this._data.set([]); }); } } + private getBodyElement(): HTMLElement | null { + if (!this.isBrowser) { + return null; + } + + return this.table()?.element?.querySelector('datatable-body') ?? null; + } + + // ngx-datatable drops its scrolling element while there are no rows, the header offset goes stale + private rememberHorizontalScrollOffset(): void { + const body = this.getBodyElement(); + this.hasPendingHorizontalScrollOffset = true; + + // Not scrollable while a previous request is still in flight, keep the offset taken back then + if (body && body.scrollWidth > body.clientWidth) { + this.horizontalScrollOffset = body.scrollLeft; + } + } + + private restoreHorizontalScrollOffset(): void { + if (!this.hasPendingHorizontalScrollOffset) { + return; + } + + this.hasPendingHorizontalScrollOffset = false; + + afterNextRender( + () => { + const body = this.getBodyElement(); + + if (!body) { + return; + } + + body.scrollLeft = this.horizontalScrollOffset; + + // The header only follows a scroll event, scrolling to the same position does not raise one + body.dispatchEvent(new Event('scroll')); + }, + { injector: this.#injector }, + ); + } + ngOnDestroy(): void { this.loadMoreSubscription.unsubscribe(); }