@shival99/z-ui 2.0.78 → 2.0.80
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.
|
@@ -6701,6 +6701,26 @@ class ZTableComponent {
|
|
|
6701
6701
|
},
|
|
6702
6702
|
];
|
|
6703
6703
|
_bulkBarExitDuration = 140;
|
|
6704
|
+
/** Stable identity key per virtual index (row/group id, not array index) so cached measured
|
|
6705
|
+
* heights follow row identity — prevents height jitter when scrolling fast. */
|
|
6706
|
+
_virtualItemKeys = computed(() => {
|
|
6707
|
+
if (!this.isVirtual()) {
|
|
6708
|
+
return [];
|
|
6709
|
+
}
|
|
6710
|
+
const flatItems = this.virtualGroupedFlatItems();
|
|
6711
|
+
if (flatItems.length > 0) {
|
|
6712
|
+
return flatItems.map((item, index) => item.type === 'group-header' ? `g:${item.groupItem.id}` : `r:${item.rows[0]?.id ?? index}`);
|
|
6713
|
+
}
|
|
6714
|
+
const groupRows = this.dynamicGroupRows();
|
|
6715
|
+
if (groupRows.length > 0) {
|
|
6716
|
+
return groupRows.map((rows, index) => `r:${rows[0]?.id ?? index}`);
|
|
6717
|
+
}
|
|
6718
|
+
return Array.from({ length: this._virtualGroupCount() }, (_, index) => index);
|
|
6719
|
+
}, ...(ngDevMode ? [{ debugName: "_virtualItemKeys" }] : []));
|
|
6720
|
+
/** Estimate height learned from real rendered rows (dynamicSize); `virtual.size` is the fallback
|
|
6721
|
+
* until a row is measured. Lets not-yet-measured rows estimate close to reality, reducing layout
|
|
6722
|
+
* shift. Plain field (non-reactive) so updating it won't rebuild the virtualizer. */
|
|
6723
|
+
_dynamicEstimateHeight = null;
|
|
6704
6724
|
/** Virtualizer instance — operates on groups rather than individual rows */
|
|
6705
6725
|
virtualizer = injectVirtualizer(() => {
|
|
6706
6726
|
const groups = this.dynamicGroups();
|
|
@@ -6710,23 +6730,30 @@ class ZTableComponent {
|
|
|
6710
6730
|
const virtualConfig = this._virtualConfig();
|
|
6711
6731
|
const flatItems = this.virtualGroupedFlatItems();
|
|
6712
6732
|
const isGrouping = flatItems.length > 0;
|
|
6733
|
+
const itemKeys = this._virtualItemKeys();
|
|
6734
|
+
const isDynamic = this.dynamicSize();
|
|
6735
|
+
// Estimate for not-yet-measured rows: learned height if available, else virtual.size.
|
|
6736
|
+
const dynamicRowEstimate = isDynamic && this._dynamicEstimateHeight !== null ? this._dynamicEstimateHeight : rowHeight;
|
|
6713
6737
|
return {
|
|
6714
6738
|
scrollElement,
|
|
6715
6739
|
count: isGrouping ? flatItems.length : groups.length > 0 ? groups.length : this._virtualGroupCount(),
|
|
6740
|
+
// Stable per-row key so cached measured heights follow row identity, not the recycled
|
|
6741
|
+
// index slot — prevents the height jitter when scrolling fast (see _virtualItemKeys).
|
|
6742
|
+
getItemKey: (index) => itemKeys[index] ?? index,
|
|
6716
6743
|
estimateSize: (index) => {
|
|
6717
6744
|
if (isGrouping) {
|
|
6718
6745
|
return flatItems[index]?.type === 'group-header'
|
|
6719
6746
|
? Math.max(rowHeight, Z_VIRTUAL_GROUP_HEADER_HEIGHT)
|
|
6720
|
-
:
|
|
6747
|
+
: dynamicRowEstimate;
|
|
6721
6748
|
}
|
|
6722
6749
|
if (groups.length > 0 && groups[index]) {
|
|
6723
6750
|
const groupHeight = groups[index].height;
|
|
6724
6751
|
if (groupHeight !== null) {
|
|
6725
6752
|
return groupHeight;
|
|
6726
6753
|
}
|
|
6727
|
-
return groups[index].rowCount *
|
|
6754
|
+
return groups[index].rowCount * dynamicRowEstimate;
|
|
6728
6755
|
}
|
|
6729
|
-
return groupSize *
|
|
6756
|
+
return groupSize * dynamicRowEstimate;
|
|
6730
6757
|
},
|
|
6731
6758
|
scrollToFn: (offset, options, instance) => {
|
|
6732
6759
|
instance.scrollOffset = offset;
|
|
@@ -6755,14 +6782,45 @@ class ZTableComponent {
|
|
|
6755
6782
|
if (!view) {
|
|
6756
6783
|
return;
|
|
6757
6784
|
}
|
|
6758
|
-
//
|
|
6785
|
+
// Only sample heights until the estimate is locked; afterwards just measure (no extra reads).
|
|
6786
|
+
const needsEstimate = this._dynamicEstimateHeight === null;
|
|
6787
|
+
// Batch measurements into the next frame to avoid repositioning multiple times per scroll frame.
|
|
6759
6788
|
const frameId = view.requestAnimationFrame(() => {
|
|
6789
|
+
let measuredSum = 0;
|
|
6790
|
+
let measuredCount = 0;
|
|
6760
6791
|
for (const el of elements) {
|
|
6761
|
-
|
|
6792
|
+
const node = el.nativeElement;
|
|
6793
|
+
this.virtualizer.measureElement(node);
|
|
6794
|
+
if (needsEstimate) {
|
|
6795
|
+
// Layout already flushed by measureElement above, so this read hits the cache (no reflow).
|
|
6796
|
+
const { height } = node.getBoundingClientRect();
|
|
6797
|
+
if (height > 0) {
|
|
6798
|
+
measuredSum += height;
|
|
6799
|
+
measuredCount++;
|
|
6800
|
+
}
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
if (needsEstimate) {
|
|
6804
|
+
this._learnDynamicEstimate(measuredSum, measuredCount);
|
|
6762
6805
|
}
|
|
6763
6806
|
});
|
|
6764
6807
|
onCleanup(() => view.cancelAnimationFrame(frameId));
|
|
6765
6808
|
}, ...(ngDevMode ? [{ debugName: "_measureVirtualItems" }] : []));
|
|
6809
|
+
/** Learn estimate height from rendered rows. Locks ONCE so the estimate doesn't jump every scroll
|
|
6810
|
+
* frame (each row is still measured exactly on render). Re-flows via virtualizer.measure(). */
|
|
6811
|
+
_learnDynamicEstimate(measuredSum, measuredCount) {
|
|
6812
|
+
if (this._dynamicEstimateHeight !== null || measuredCount === 0) {
|
|
6813
|
+
return;
|
|
6814
|
+
}
|
|
6815
|
+
const average = Math.round(measuredSum / measuredCount);
|
|
6816
|
+
// Skip re-flow when already equal to the default size.
|
|
6817
|
+
if (average <= 0 || average === this.virtualRowHeight()) {
|
|
6818
|
+
this._dynamicEstimateHeight = average > 0 ? average : null;
|
|
6819
|
+
return;
|
|
6820
|
+
}
|
|
6821
|
+
this._dynamicEstimateHeight = average;
|
|
6822
|
+
this.virtualizer.measure();
|
|
6823
|
+
}
|
|
6766
6824
|
_rowDragAutoScroll = effect(onCleanup => {
|
|
6767
6825
|
const scrollbar = this.tbodyScrollbar();
|
|
6768
6826
|
if (!scrollbar?.adapter.initialized()) {
|
|
@@ -7018,6 +7076,8 @@ class ZTableComponent {
|
|
|
7018
7076
|
}
|
|
7019
7077
|
});
|
|
7020
7078
|
explicitEffect([this.columnFilters, this.globalFilter, this.pagination, this.sorting, this._data], () => {
|
|
7079
|
+
// New dataset may have different row heights -> relearn the estimate.
|
|
7080
|
+
this._dynamicEstimateHeight = null;
|
|
7021
7081
|
queueMicrotask(() => {
|
|
7022
7082
|
this._checkVerticalScroll();
|
|
7023
7083
|
this._checkHorizontalScroll();
|