@meshmakers/shared-ui 3.4.450 → 3.4.470

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.
@@ -1685,6 +1685,17 @@ class ListViewComponent extends CommandBaseService {
1685
1685
  resizeObserver;
1686
1686
  /** Never auto-page below this many rows, even in a tiny viewport. */
1687
1687
  static MIN_AUTO_PAGE_SIZE = 5;
1688
+ /**
1689
+ * Minimum change (in rows) before an already-measured `autoPageSize` is
1690
+ * re-applied. Rows can vary in height (component cells such as sparklines,
1691
+ * wrapping text) so the derived fit-to-height count jitters by ±1 as the user
1692
+ * pages through content. Without this dead-band every page change would
1693
+ * re-derive the page size and fire an extra fetch with a realigned `skip`,
1694
+ * overlapping the page the user actually navigated to. The very first
1695
+ * measurement always applies (it converts the initial default into the fitted
1696
+ * size); genuine resizes/density changes move by more than this threshold.
1697
+ */
1698
+ static AUTO_PAGE_SIZE_HYSTERESIS = 2;
1688
1699
  /** Measured fit-to-height page size (`autoPageSize`); null until the first measurement. */
1689
1700
  autoPageSizeValue = signal(null, /* @ts-ignore */
1690
1701
  ...(ngDevMode ? [{ debugName: "autoPageSizeValue" }] : /* istanbul ignore next */ []));
@@ -1837,9 +1848,14 @@ class ListViewComponent extends CommandBaseService {
1837
1848
  if (this.dataBindingDirective) {
1838
1849
  this.dataBindingDirective.isLoading$.pipe(observeOn(asyncScheduler), takeUntil(this.destroy$)).subscribe(loading => {
1839
1850
  this.isLoading.set(loading);
1840
- // Rows just (re)rendered row height may have changed (density switch,
1841
- // first data arrival), so re-measure the fit-to-height page size.
1842
- if (!loading && this.autoPageSize) {
1851
+ // Measure the fit-to-height page size only ONCE, when data first
1852
+ // renders (initial default fitted). After that, page-size changes come
1853
+ // solely from the ResizeObserver (genuine viewport/density changes).
1854
+ // Re-measuring after every load would let per-page row-height
1855
+ // differences (wrapping descriptions, sparklines) re-derive the page
1856
+ // size mid-navigation, firing a second fetch that realigns `skip` and
1857
+ // overlaps the page the user just opened.
1858
+ if (!loading && this.autoPageSize && this.autoPageSizeValue() === null) {
1843
1859
  this.autoPageSizeRecompute$.next();
1844
1860
  }
1845
1861
  });
@@ -1892,14 +1908,35 @@ class ListViewComponent extends CommandBaseService {
1892
1908
  if (!content) {
1893
1909
  return;
1894
1910
  }
1895
- const row = content.querySelector('tbody tr');
1896
- const rowHeight = row?.getBoundingClientRect().height || this.lastMeasuredRowHeight;
1911
+ // Measure the TALLEST rendered row, not just the first one. Rows in a list
1912
+ // can vary greatly in height (component cells such as sparklines / last-run,
1913
+ // wrapping descriptions), and the first row's height differs from page to
1914
+ // page. Measuring only the first row made the derived page size swing
1915
+ // between pages; keying off the tallest row keeps it stable and guarantees
1916
+ // a full page never overflows into a scrollbar.
1917
+ let measuredRowHeight = 0;
1918
+ content.querySelectorAll('tbody tr').forEach((row) => {
1919
+ const height = row.getBoundingClientRect().height;
1920
+ if (height > measuredRowHeight) {
1921
+ measuredRowHeight = height;
1922
+ }
1923
+ });
1924
+ const rowHeight = measuredRowHeight || this.lastMeasuredRowHeight;
1897
1925
  if (!rowHeight || content.clientHeight <= 0) {
1898
1926
  return;
1899
1927
  }
1900
1928
  this.lastMeasuredRowHeight = rowHeight;
1901
1929
  const take = Math.max(ListViewComponent.MIN_AUTO_PAGE_SIZE, Math.floor(content.clientHeight / rowHeight));
1902
- if (take === this.effectivePageSize()) {
1930
+ const current = this.effectivePageSize();
1931
+ if (take === current) {
1932
+ return;
1933
+ }
1934
+ // After the first measurement, ignore small (±1) changes: they come from
1935
+ // per-page row-height jitter, and applying them would fire an extra fetch
1936
+ // that realigns `skip` and overlaps the page the user just opened. Genuine
1937
+ // resizes/density changes exceed the hysteresis band and still apply.
1938
+ if (this.autoPageSizeValue() !== null &&
1939
+ Math.abs(take - current) < ListViewComponent.AUTO_PAGE_SIZE_HYSTERESIS) {
1903
1940
  return;
1904
1941
  }
1905
1942
  this.autoPageSizeValue.set(take);