@angular/cdk 18.2.10 → 18.2.11

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.
@@ -663,6 +663,12 @@ class StickyStyler {
663
663
  this._isBrowser = _isBrowser;
664
664
  this._needsPositionStickyOnElement = _needsPositionStickyOnElement;
665
665
  this._positionListener = _positionListener;
666
+ this._elemSizeCache = new WeakMap();
667
+ this._resizeObserver = globalThis?.ResizeObserver
668
+ ? new globalThis.ResizeObserver(entries => this._updateCachedSizes(entries))
669
+ : null;
670
+ this._updatedStickyColumnsParamsToReplay = [];
671
+ this._stickyColumnsReplayTimeout = null;
666
672
  this._cachedCellWidths = [];
667
673
  this._borderCellCss = {
668
674
  'top': `${_stickCellCss}-border-elem-top`,
@@ -678,6 +684,9 @@ class StickyStyler {
678
684
  * @param stickyDirections The directions that should no longer be set as sticky on the rows.
679
685
  */
680
686
  clearStickyPositioning(rows, stickyDirections) {
687
+ if (stickyDirections.includes('left') || stickyDirections.includes('right')) {
688
+ this._removeFromStickyColumnReplayQueue(rows);
689
+ }
681
690
  const elementsToClear = [];
682
691
  for (const row of rows) {
683
692
  // If the row isn't an element (e.g. if it's an `ng-container`),
@@ -707,8 +716,16 @@ class StickyStyler {
707
716
  * in this index position should be stuck to the end of the row.
708
717
  * @param recalculateCellWidths Whether the sticky styler should recalculate the width of each
709
718
  * column cell. If `false` cached widths will be used instead.
719
+ * @param replay Whether to enqueue this call for replay after a ResizeObserver update.
710
720
  */
711
- updateStickyColumns(rows, stickyStartStates, stickyEndStates, recalculateCellWidths = true) {
721
+ updateStickyColumns(rows, stickyStartStates, stickyEndStates, recalculateCellWidths = true, replay = true) {
722
+ if (replay) {
723
+ this._updateStickyColumnReplayQueue({
724
+ rows: [...rows],
725
+ stickyStartStates: [...stickyStartStates],
726
+ stickyEndStates: [...stickyEndStates],
727
+ });
728
+ }
712
729
  if (!rows.length ||
713
730
  !this._isBrowser ||
714
731
  !(stickyStartStates.some(state => state) || stickyEndStates.some(state => state))) {
@@ -797,7 +814,7 @@ class StickyStyler {
797
814
  elementsToStick[rowIndex] = this._isNativeHtmlTable
798
815
  ? Array.from(row.children)
799
816
  : [row];
800
- const height = row.getBoundingClientRect().height;
817
+ const height = this._retrieveElementSize(row).height;
801
818
  stickyOffset += height;
802
819
  stickyCellHeights[rowIndex] = height;
803
820
  }
@@ -931,8 +948,8 @@ class StickyStyler {
931
948
  const cellWidths = [];
932
949
  const firstRowCells = row.children;
933
950
  for (let i = 0; i < firstRowCells.length; i++) {
934
- let cell = firstRowCells[i];
935
- cellWidths.push(cell.getBoundingClientRect().width);
951
+ const cell = firstRowCells[i];
952
+ cellWidths.push(this._retrieveElementSize(cell).width);
936
953
  }
937
954
  this._cachedCellWidths = cellWidths;
938
955
  return cellWidths;
@@ -969,6 +986,79 @@ class StickyStyler {
969
986
  }
970
987
  return positions;
971
988
  }
989
+ /**
990
+ * Retreives the most recently observed size of the specified element from the cache, or
991
+ * meaures it directly if not yet cached.
992
+ */
993
+ _retrieveElementSize(element) {
994
+ const cachedSize = this._elemSizeCache.get(element);
995
+ if (cachedSize) {
996
+ return cachedSize;
997
+ }
998
+ const clientRect = element.getBoundingClientRect();
999
+ const size = { width: clientRect.width, height: clientRect.height };
1000
+ if (!this._resizeObserver) {
1001
+ return size;
1002
+ }
1003
+ this._elemSizeCache.set(element, size);
1004
+ this._resizeObserver.observe(element, { box: 'border-box' });
1005
+ return size;
1006
+ }
1007
+ /**
1008
+ * Conditionally enqueue the requested sticky update and clear previously queued updates
1009
+ * for the same rows.
1010
+ */
1011
+ _updateStickyColumnReplayQueue(params) {
1012
+ this._removeFromStickyColumnReplayQueue(params.rows);
1013
+ // No need to replay if a flush is pending.
1014
+ if (this._stickyColumnsReplayTimeout) {
1015
+ return;
1016
+ }
1017
+ this._updatedStickyColumnsParamsToReplay.push(params);
1018
+ }
1019
+ /** Remove updates for the specified rows from the queue. */
1020
+ _removeFromStickyColumnReplayQueue(rows) {
1021
+ const rowsSet = new Set(rows);
1022
+ for (const update of this._updatedStickyColumnsParamsToReplay) {
1023
+ update.rows = update.rows.filter(row => !rowsSet.has(row));
1024
+ }
1025
+ this._updatedStickyColumnsParamsToReplay = this._updatedStickyColumnsParamsToReplay.filter(update => !!update.rows.length);
1026
+ }
1027
+ /** Update _elemSizeCache with the observed sizes. */
1028
+ _updateCachedSizes(entries) {
1029
+ let needsColumnUpdate = false;
1030
+ for (const entry of entries) {
1031
+ const newEntry = entry.borderBoxSize?.length
1032
+ ? {
1033
+ width: entry.borderBoxSize[0].inlineSize,
1034
+ height: entry.borderBoxSize[0].blockSize,
1035
+ }
1036
+ : {
1037
+ width: entry.contentRect.width,
1038
+ height: entry.contentRect.height,
1039
+ };
1040
+ if (newEntry.width !== this._elemSizeCache.get(entry.target)?.width &&
1041
+ isCell(entry.target)) {
1042
+ needsColumnUpdate = true;
1043
+ }
1044
+ this._elemSizeCache.set(entry.target, newEntry);
1045
+ }
1046
+ if (needsColumnUpdate && this._updatedStickyColumnsParamsToReplay.length) {
1047
+ if (this._stickyColumnsReplayTimeout) {
1048
+ clearTimeout(this._stickyColumnsReplayTimeout);
1049
+ }
1050
+ this._stickyColumnsReplayTimeout = setTimeout(() => {
1051
+ for (const update of this._updatedStickyColumnsParamsToReplay) {
1052
+ this.updateStickyColumns(update.rows, update.stickyStartStates, update.stickyEndStates, true, false);
1053
+ }
1054
+ this._updatedStickyColumnsParamsToReplay = [];
1055
+ this._stickyColumnsReplayTimeout = null;
1056
+ }, 0);
1057
+ }
1058
+ }
1059
+ }
1060
+ function isCell(element) {
1061
+ return ['cdk-cell', 'cdk-header-cell', 'cdk-footer-cell'].some(klass => element.classList.contains(klass));
972
1062
  }
973
1063
 
974
1064
  /**