@humanspeak/svelte-virtual-list 0.4.4 → 0.4.5

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.
@@ -62,6 +62,11 @@ export declare const calculateScrollPosition: (totalItems: number, itemHeight: n
62
62
  * @param {number} totalItems - Total number of items in the list
63
63
  * @param {number} bufferSize - Number of items to render outside the visible area
64
64
  * @param {SvelteVirtualListMode} mode - Scroll direction mode
65
+ * @param {boolean} atBottom - Whether the list is scrolled to the bottom (unused, legacy parameter)
66
+ * @param {boolean} wasAtBottomBeforeHeightChange - Whether the list was at bottom before a height change (unused, legacy parameter)
67
+ * @param {SvelteVirtualListPreviousVisibleRange | null} lastVisibleRange - Previous visible range (unused, legacy parameter)
68
+ * @param {number} [totalContentHeight] - Pre-calculated total content height; defaults to totalItems * itemHeight
69
+ * @param {Record<number, number>} [heightCache] - Cache of measured item heights keyed by index, used in topToBottom mode to walk actual heights instead of dividing by average
65
70
  * @returns {SvelteVirtualListPreviousVisibleRange} Range of indices to render
66
71
  */
67
72
  export declare const calculateVisibleRange: (scrollTop: number, viewportHeight: number, itemHeight: number, totalItems: number, bufferSize: number, mode: SvelteVirtualListMode, atBottom: boolean, wasAtBottomBeforeHeightChange: boolean, lastVisibleRange: SvelteVirtualListPreviousVisibleRange | null, totalContentHeight?: number, heightCache?: Record<number, number>) => SvelteVirtualListPreviousVisibleRange;
@@ -66,6 +66,11 @@ export const calculateScrollPosition = (totalItems, itemHeight, containerHeight)
66
66
  * @param {number} totalItems - Total number of items in the list
67
67
  * @param {number} bufferSize - Number of items to render outside the visible area
68
68
  * @param {SvelteVirtualListMode} mode - Scroll direction mode
69
+ * @param {boolean} atBottom - Whether the list is scrolled to the bottom (unused, legacy parameter)
70
+ * @param {boolean} wasAtBottomBeforeHeightChange - Whether the list was at bottom before a height change (unused, legacy parameter)
71
+ * @param {SvelteVirtualListPreviousVisibleRange | null} lastVisibleRange - Previous visible range (unused, legacy parameter)
72
+ * @param {number} [totalContentHeight] - Pre-calculated total content height; defaults to totalItems * itemHeight
73
+ * @param {Record<number, number>} [heightCache] - Cache of measured item heights keyed by index, used in topToBottom mode to walk actual heights instead of dividing by average
69
74
  * @returns {SvelteVirtualListPreviousVisibleRange} Range of indices to render
70
75
  */
71
76
  export const calculateVisibleRange = (scrollTop, viewportHeight, itemHeight, totalItems, bufferSize, mode, atBottom, wasAtBottomBeforeHeightChange, lastVisibleRange, totalContentHeight, heightCache) => {
@@ -92,8 +97,25 @@ export const calculateVisibleRange = (scrollTop, viewportHeight, itemHeight, tot
92
97
  return { start, end };
93
98
  }
94
99
  else {
95
- const start = Math.floor(scrollTop / itemHeight);
96
- const end = Math.min(totalItems, start + Math.ceil(viewportHeight / itemHeight) + 1);
100
+ // Walk forward through measured heights to find the correct start index
101
+ // instead of dividing by average height (which is wrong for variable-height items).
102
+ let start = 0;
103
+ let acc = 0;
104
+ while (start < totalItems) {
105
+ const h = getValidHeight(heightCache?.[start], itemHeight);
106
+ if (acc + h > scrollTop)
107
+ break;
108
+ acc += h;
109
+ start++;
110
+ }
111
+ // Walk forward from start to find end
112
+ let end = start;
113
+ let viewAcc = 0;
114
+ while (end < totalItems && viewAcc < viewportHeight) {
115
+ viewAcc += getValidHeight(heightCache?.[end], itemHeight);
116
+ end++;
117
+ }
118
+ end = Math.min(totalItems, end + 1); // +1 to ensure partial items are visible
97
119
  // Safeguard for topToBottom: ensure last item is fully visible when at max scroll
98
120
  const totalHeight = totalContentHeight ?? totalItems * itemHeight;
99
121
  const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
@@ -104,11 +126,9 @@ export const calculateVisibleRange = (scrollTop, viewportHeight, itemHeight, tot
104
126
  // Pack from the end using measured heights when available: walk backward until viewport filled
105
127
  const adjustedEnd = totalItems;
106
128
  let startCore = adjustedEnd;
107
- let acc = 0;
108
- const getH = (i) => getValidHeight(heightCache ? heightCache[i] : undefined, itemHeight);
109
- while (startCore > 0 && acc < viewportHeight) {
110
- const h = getH(startCore - 1);
111
- acc += h;
129
+ let backAcc = 0;
130
+ while (startCore > 0 && backAcc < viewportHeight) {
131
+ backAcc += getValidHeight(heightCache?.[startCore - 1], itemHeight);
112
132
  startCore -= 1;
113
133
  }
114
134
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanspeak/svelte-virtual-list",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "A lightweight, high-performance virtual list component for Svelte 5 that renders large datasets with minimal memory usage. Features include dynamic height support, smooth scrolling, TypeScript support, and efficient DOM recycling. Ideal for infinite scrolling lists, data tables, chat interfaces, and any application requiring the rendering of thousands of items without compromising performance. Zero dependencies and fully customizable.",
5
5
  "keywords": [
6
6
  "svelte",