@humanspeak/svelte-virtual-list 0.3.13 → 0.4.0

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.
@@ -251,11 +251,11 @@
251
251
 
252
252
  const captureAnchor = () => {
253
253
  if (!heightManager.viewportElement) return
254
- const vr = visibleItems()
254
+ const vr = visibleItems
255
255
  const anchorIndex = Math.max(0, vr.start)
256
256
  const cache = heightManager.getHeightCache()
257
257
  const est = heightManager.averageHeight
258
- const maxScrollTop = Math.max(0, totalHeight() - (height || 0))
258
+ const maxScrollTop = Math.max(0, totalHeight - (height || 0))
259
259
  // Offset from start to anchored item
260
260
  const blockSums = buildBlockSums(cache, est, items.length)
261
261
  const offsetToIndex = getScrollOffsetForIndex(cache, est, anchorIndex, blockSums)
@@ -290,7 +290,7 @@
290
290
  Math.max(0, lastAnchorIndex),
291
291
  blockSums
292
292
  )
293
- const maxScrollTop = clampValue(totalHeight() - (height || 0), 0, Infinity)
293
+ const maxScrollTop = clampValue(totalHeight - (height || 0), 0, Infinity)
294
294
  let targetTop: number
295
295
  if (mode === 'bottomToTop') {
296
296
  const distanceFromStart = clampValue(offsetToIndex + lastAnchorOffset, 0, Infinity)
@@ -402,23 +402,6 @@
402
402
  // Dynamic update coordination to avoid UA scroll anchoring interference
403
403
  let suppressBottomAnchoringUntilMs = $state(0)
404
404
 
405
- const displayItems = $derived(() => {
406
- const visibleRange = visibleItems()
407
- const slice =
408
- mode === 'bottomToTop'
409
- ? items.slice(visibleRange.start, visibleRange.end).reverse()
410
- : items.slice(visibleRange.start, visibleRange.end)
411
-
412
- return slice.map((item, sliceIndex) => ({
413
- item,
414
- originalIndex:
415
- mode === 'bottomToTop'
416
- ? visibleRange.end - 1 - sliceIndex
417
- : visibleRange.start + sliceIndex,
418
- sliceIndex
419
- }))
420
- })
421
-
422
405
  /**
423
406
  * Handles scroll position corrections when item heights change, ensuring proper positioning
424
407
  * relative to the user's scroll context. This function calculates the cumulative impact of
@@ -442,7 +425,7 @@
442
425
  if (isScrolling) {
443
426
  // Accumulate net change above viewport and defer application
444
427
  let pending = 0
445
- const currentVisibleRange = visibleItems()
428
+ const currentVisibleRange = visibleItems
446
429
  for (const change of heightChanges) {
447
430
  if (change.index < currentVisibleRange.start) pending += change.delta
448
431
  }
@@ -488,7 +471,7 @@
488
471
  *
489
472
  * Dependencies:
490
473
  * - wasAtBottomBeforeHeightChange: Set to true when first item marked dirty, prevents cascading corrections
491
- * - totalHeight(): Uses actual heightCache measurements instead of skewed averages
474
+ * - totalHeight: Uses actual heightCache measurements instead of skewed averages
492
475
  * - Aggressive scroll correction: Blocked when wasAtBottomBeforeHeightChange=true
493
476
  *
494
477
  * ⚠️ DO NOT MODIFY WITHOUT EXTENSIVE TESTING ⚠️
@@ -518,7 +501,7 @@
518
501
  lastCorrectionTimestampByViewport.set(viewportEl, now)
519
502
 
520
503
  // Step 1: Scroll to approximate position to ensure Item 0 gets rendered in virtual viewport
521
- const approximateScrollTop = Math.max(0, totalHeight() - height)
504
+ const approximateScrollTop = Math.max(0, totalHeight - height)
522
505
  log('[SVL] b2t-correction-approx', { approximateScrollTop })
523
506
  syncScrollTop(approximateScrollTop)
524
507
 
@@ -555,11 +538,11 @@
555
538
  }
556
539
 
557
540
  const currentScrollTop = heightManager.viewport.scrollTop
558
- const maxScrollTop = Math.max(0, totalHeight() - height)
541
+ const maxScrollTop = Math.max(0, totalHeight - height)
559
542
 
560
543
  // Calculate total height change impact above current visible area
561
544
  let heightChangeAboveViewport = 0
562
- const currentVisibleRange = visibleItems()
545
+ const currentVisibleRange = visibleItems
563
546
 
564
547
  for (const change of heightChanges) {
565
548
  // Only consider items that are above the current visible range
@@ -623,7 +606,7 @@
623
606
  // Skip loading during bottomToTop initialization (init path renders all items artificially)
624
607
  if (mode === 'bottomToTop' && !bottomToTopScrollComplete) return
625
608
 
626
- const range = visibleItems()
609
+ const range = visibleItems
627
610
  const atLoadingEdge = range.end >= items.length - loadMoreThreshold
628
611
  const insufficientItems = items.length < loadMoreThreshold && heightManager.initialized
629
612
 
@@ -755,9 +738,9 @@
755
738
  * This getter is reactive and updates whenever heightManager's internal state changes.
756
739
  * Used by: atBottom calculation, scroll corrections, maxScrollTop calculations
757
740
  */
758
- const totalHeight = $derived(() => heightManager.totalHeight)
741
+ const totalHeight = $derived(heightManager.totalHeight)
759
742
 
760
- const atBottom = $derived(heightManager.scrollTop >= totalHeight() - height - 1)
743
+ const atBottom = $derived(heightManager.scrollTop >= totalHeight - height - 1)
761
744
  let wasAtBottomBeforeHeightChange = false
762
745
  let lastVisibleRange: SvelteVirtualListPreviousVisibleRange | null = null
763
746
 
@@ -787,7 +770,7 @@
787
770
  mode === 'bottomToTop' &&
788
771
  heightManager.viewportElement
789
772
  ) {
790
- const targetScrollTop = Math.max(0, totalHeight() - height)
773
+ const targetScrollTop = Math.max(0, totalHeight - height)
791
774
  const currentScrollTop = heightManager.viewport.scrollTop
792
775
  const scrollDifference = Math.abs(currentScrollTop - targetScrollTop)
793
776
 
@@ -797,7 +780,7 @@
797
780
  // 3. We're significantly off target
798
781
  // 4. We're not at the bottom (where height changes should be handled more carefully)
799
782
  const heightChanged = Math.abs(heightManager.averageHeight - lastCalculatedHeight) > 1
800
- const maxScrollTop = Math.max(0, totalHeight() - height)
783
+ const maxScrollTop = Math.max(0, totalHeight - height)
801
784
 
802
785
  // In bottomToTop mode, we're "at bottom" when scroll is at max position
803
786
  const isAtBottom =
@@ -845,7 +828,7 @@
845
828
  const currentScrollTop = heightManager.viewport.scrollTop
846
829
  const currentCalculatedItemHeight = heightManager.averageHeight
847
830
  const currentHeight = height
848
- const currentTotalHeight = totalHeight()
831
+ const currentTotalHeight = totalHeight
849
832
  const prevTotalHeight =
850
833
  lastTotalHeightObserved ||
851
834
  currentTotalHeight - itemsAdded * currentCalculatedItemHeight
@@ -891,7 +874,7 @@
891
874
  // Reconcile on next frame in case measured heights adjust totals
892
875
  requestAnimationFrame(() => {
893
876
  const beforeReconcileScrollTop = heightManager.viewport.scrollTop
894
- const reconciledNextMax = clampValue(totalHeight() - height, 0, Infinity)
877
+ const reconciledNextMax = clampValue(totalHeight - height, 0, Infinity)
895
878
  const reconciledDeltaMaxChange = reconciledNextMax - nextMaxScrollTop
896
879
  // Desired position is to maintain distance-from-end; equivalently keep (max - scrollTop) constant.
897
880
  const desiredScrollTop = clampValue(
@@ -934,7 +917,7 @@
934
917
 
935
918
  lastItemsLength = currentItemsLength
936
919
  // Update last observed total height at the end of the effect
937
- lastTotalHeightObserved = totalHeight()
920
+ lastTotalHeightObserved = totalHeight
938
921
  })
939
922
 
940
923
  // Update container height continuously to reflect layout changes that
@@ -970,13 +953,13 @@
970
953
  *
971
954
  * @example
972
955
  * ```typescript
973
- * const range = visibleItems()
956
+ * const range = visibleItems
974
957
  * console.info(`Rendering items from ${range.start} to ${range.end}`)
975
958
  * ```
976
959
  *
977
960
  * @returns {SvelteVirtualListPreviousVisibleRange} Object containing start and end indices of visible items
978
961
  */
979
- const visibleItems = $derived((): SvelteVirtualListPreviousVisibleRange => {
962
+ const visibleItems = $derived.by((): SvelteVirtualListPreviousVisibleRange => {
980
963
  if (!items.length) return { start: 0, end: 0 } as SvelteVirtualListPreviousVisibleRange
981
964
  const viewportHeight = height || 0
982
965
 
@@ -1021,7 +1004,7 @@
1021
1004
  atBottom,
1022
1005
  wasAtBottomBeforeHeightChange,
1023
1006
  lastVisibleRange,
1024
- totalHeight(),
1007
+ totalHeight,
1025
1008
  heightManager.getHeightCache()
1026
1009
  )
1027
1010
 
@@ -1033,15 +1016,15 @@
1033
1016
  * Uses the maximum of container height and total content height to ensure
1034
1017
  * proper scrolling behavior.
1035
1018
  */
1036
- const contentHeight = $derived(() => Math.max(height, totalHeight()))
1019
+ const contentHeight = $derived(Math.max(height, totalHeight))
1037
1020
 
1038
1021
  /**
1039
1022
  * Computed transform Y value for positioning the visible items.
1040
1023
  * Extracted from inline IIFE for better performance and readability.
1041
1024
  */
1042
- const transformY = $derived(() => {
1025
+ const transformY = $derived.by(() => {
1043
1026
  const viewportHeight = height || measuredFallbackHeight || 0
1044
- const visibleRange = visibleItems()
1027
+ const visibleRange = visibleItems
1045
1028
 
1046
1029
  // Avoid synchronous DOM reads here; fall back once if height is 0
1047
1030
  const effectiveHeight = viewportHeight === 0 ? 400 : viewportHeight
@@ -1055,13 +1038,30 @@
1055
1038
  visibleRange.start,
1056
1039
  heightManager.averageHeight,
1057
1040
  effectiveHeight,
1058
- totalHeight(),
1041
+ totalHeight,
1059
1042
  heightManager.getHeightCache(),
1060
1043
  measuredFallbackHeight
1061
1044
  )
1062
1045
  )
1063
1046
  })
1064
1047
 
1048
+ const displayItems = $derived.by(() => {
1049
+ const visibleRange = visibleItems
1050
+ const slice =
1051
+ mode === 'bottomToTop'
1052
+ ? items.slice(visibleRange.start, visibleRange.end).reverse()
1053
+ : items.slice(visibleRange.start, visibleRange.end)
1054
+
1055
+ return slice.map((item, sliceIndex) => ({
1056
+ item,
1057
+ originalIndex:
1058
+ mode === 'bottomToTop'
1059
+ ? visibleRange.end - 1 - sliceIndex
1060
+ : visibleRange.start + sliceIndex,
1061
+ sliceIndex
1062
+ }))
1063
+ })
1064
+
1065
1065
  /**
1066
1066
  * Handles scroll events in the viewport using requestAnimationFrame for performance.
1067
1067
  *
@@ -1124,12 +1124,12 @@
1124
1124
  captureAnchor()
1125
1125
  }
1126
1126
  if (INTERNAL_DEBUG) {
1127
- const vr = visibleItems()
1127
+ const vr = visibleItems
1128
1128
  log('[SVL] scroll', {
1129
1129
  mode,
1130
1130
  scrollTop: heightManager.scrollTop,
1131
1131
  height,
1132
- totalHeight: totalHeight(),
1132
+ totalHeight: totalHeight,
1133
1133
  averageItemHeight: heightManager.averageHeight,
1134
1134
  visibleRange: vr
1135
1135
  })
@@ -1160,7 +1160,7 @@
1160
1160
  })
1161
1161
  if (!heightManager.initialized && mode === 'bottomToTop') {
1162
1162
  // bottomToTop initialization: use scrollIntoView on Item 0 for precise positioning
1163
- // visibleItems() guarantees Item 0 is rendered during initialization
1163
+ // visibleItems guarantees Item 0 is rendered during initialization
1164
1164
  tick().then(() => {
1165
1165
  requestAnimationFrame(() => {
1166
1166
  requestAnimationFrame(() => {
@@ -1181,7 +1181,7 @@
1181
1181
 
1182
1182
  setTimeout(() => {
1183
1183
  // Step 1: Set initialized (for other purposes like scroll event handling)
1184
- // The init path in visibleItems() stays active until bottomToTopScrollComplete
1184
+ // The init path in visibleItems stays active until bottomToTopScrollComplete
1185
1185
  if (!heightManager.initialized) {
1186
1186
  heightManager.initialized = true
1187
1187
  }
@@ -1265,7 +1265,7 @@
1265
1265
  rafSchedule(() => {
1266
1266
  log('item-resize-observer', { entries: entries.length })
1267
1267
  let shouldRecalculate = false
1268
- void visibleItems() // Cache once to avoid reactive loops
1268
+ void visibleItems // Cache once to avoid reactive loops
1269
1269
 
1270
1270
  for (const entry of entries) {
1271
1271
  const element = entry.target as HTMLElement
@@ -1349,7 +1349,7 @@
1349
1349
  // Add the effect in the script section
1350
1350
  $effect(() => {
1351
1351
  if (INTERNAL_DEBUG) {
1352
- prevVisibleRange = visibleItems()
1352
+ prevVisibleRange = visibleItems
1353
1353
  prevHeight = heightManager.averageHeight
1354
1354
  }
1355
1355
  })
@@ -1358,7 +1358,7 @@
1358
1358
  // the callback writes to $state (which is forbidden during render effects)
1359
1359
  $effect(() => {
1360
1360
  if (!debug) return
1361
- const currentVisibleRange = visibleItems()
1361
+ const currentVisibleRange = visibleItems
1362
1362
  if (
1363
1363
  !shouldShowDebugInfo(
1364
1364
  prevVisibleRange,
@@ -1376,7 +1376,7 @@
1376
1376
  heightManager.averageHeight,
1377
1377
  heightManager.scrollTop,
1378
1378
  height || 0,
1379
- totalHeight()
1379
+ totalHeight
1380
1380
  )
1381
1381
 
1382
1382
  if (debugFunction) {
@@ -1484,7 +1484,7 @@
1484
1484
  }
1485
1485
  }
1486
1486
 
1487
- const { start: firstVisibleIndex, end: lastVisibleIndex } = visibleItems()
1487
+ const { start: firstVisibleIndex, end: lastVisibleIndex } = visibleItems
1488
1488
 
1489
1489
  // Use extracted scroll calculation utility
1490
1490
  const scrollTarget = calculateScrollTarget({
@@ -1630,16 +1630,16 @@
1630
1630
  id="virtual-list-content"
1631
1631
  {...testId ? { 'data-testid': `${testId}-content` } : {}}
1632
1632
  class={contentClass ?? 'virtual-list-content'}
1633
- style:height="{contentHeight()}px"
1633
+ style:height="{contentHeight}px"
1634
1634
  >
1635
1635
  <!-- Items container is translated to show correct items -->
1636
1636
  <div
1637
1637
  id="virtual-list-items"
1638
1638
  {...testId ? { 'data-testid': `${testId}-items` } : {}}
1639
1639
  class={itemsClass ?? 'virtual-list-items'}
1640
- style:transform="translateY({transformY()}px)"
1640
+ style:transform="translateY({transformY}px)"
1641
1641
  >
1642
- {#each displayItems() as currentItemWithIndex, _i (currentItemWithIndex.originalIndex)}
1642
+ {#each displayItems as currentItemWithIndex, _i (currentItemWithIndex.originalIndex)}
1643
1643
  <!-- Render each visible item -->
1644
1644
  <div
1645
1645
  bind:this={itemElements[currentItemWithIndex.sliceIndex]}
@@ -71,7 +71,7 @@ const updateHeight = () => {
71
71
 
72
72
  ```typescript
73
73
  // OLD: O(n) calculation every time
74
- // let totalHeight = $derived(() => {
74
+ // let totalHeight = $derived.by(() => {
75
75
  // let total = 0
76
76
  // for (let i = 0; i < items.length; i++) {
77
77
  // total += heightCache[i] || calculatedItemHeight
@@ -80,7 +80,7 @@ const updateHeight = () => {
80
80
  // })
81
81
 
82
82
  // NEW: O(1) reactive calculation 🚀
83
- let totalHeight = $derived(() => heightManager.totalHeight)
83
+ let totalHeight = $derived(heightManager.totalHeight)
84
84
  ```
85
85
 
86
86
  ## Performance Benefits
@@ -32,7 +32,7 @@ ReactiveListManager processes only **dirty/changed items**:
32
32
  ```typescript
33
33
  // ✅ O(dirty items) - Fast and reactive
34
34
  manager.processDirtyHeights(changedItems)
35
- const totalHeight = manager.getDerivedTotalHeight()
35
+ const totalHeight = manager.totalHeight
36
36
  ```
37
37
 
38
38
  ## 📦 Installation
@@ -82,7 +82,7 @@ new ReactiveListManager(config: ListManagerConfig)
82
82
  **Parameters:**
83
83
 
84
84
  - `config.itemLength` - Total number of items
85
- - `config.estimatedHeight` - Default height for unmeasured items
85
+ - `config.itemHeight` - Default height for unmeasured items
86
86
 
87
87
  ### Core Methods
88
88
 
@@ -154,7 +154,7 @@ Check if manager has sufficient measurement data.
154
154
  // Create manager
155
155
  const heightManager = new ReactiveListManager({
156
156
  itemLength: items.length,
157
- estimatedHeight: defaultEstimatedItemHeight
157
+ itemHeight: defaultEstimatedItemHeight
158
158
  })
159
159
 
160
160
  // Update on items change
@@ -182,7 +182,7 @@ $effect(() => {
182
182
  })
183
183
 
184
184
  // Reactive total height (automatically updates)
185
- let totalHeight = $derived(() => heightManager.totalHeight)
185
+ let totalHeight = $derived(heightManager.totalHeight)
186
186
  ```
187
187
 
188
188
  ### Standalone Usage
@@ -190,7 +190,7 @@ let totalHeight = $derived(() => heightManager.totalHeight)
190
190
  ```typescript
191
191
  import { ReactiveListManager, benchmarkHeightManager } from './reactive-list-manager'
192
192
 
193
- const manager = new ReactiveListManager({ itemLength: 1000, estimatedHeight: 50 })
193
+ const manager = new ReactiveListManager({ itemLength: 1000, itemHeight: 50 })
194
194
 
195
195
  // Performance monitoring
196
196
  const results = benchmarkHeightManager(10000, 1000, 100)
@@ -264,7 +264,7 @@ npm run test -- --grep "Performance Tests"
264
264
 
265
265
  ```text
266
266
 
267
- Height Changes → processDirtyHeights() → Update State → getDerivedTotalHeight() → Reactive UI
267
+ Height Changes → processDirtyHeights() → Update State → totalHeight → Reactive UI
268
268
 
269
269
  ```
270
270
 
@@ -274,7 +274,7 @@ Height Changes → processDirtyHeights() → Update State → getDerivedTotalHei
274
274
  private _totalMeasuredHeight = $state(0) // Sum of all measured heights
275
275
  private _measuredCount = $state(0) // Count of measured items
276
276
  private _itemLength = $state(0) // Total items
277
- private _estimatedHeight = $state(40) // Default estimate
277
+ private _itemHeight = $state(40) // Default estimate
278
278
  ```
279
279
 
280
280
  ## 🔧 Types
@@ -288,7 +288,7 @@ interface HeightChange {
288
288
 
289
289
  interface ListManagerConfig {
290
290
  itemLength: number
291
- estimatedHeight: number
291
+ itemHeight: number
292
292
  }
293
293
 
294
294
  interface ListManagerDebugInfo {
@@ -296,7 +296,7 @@ interface ListManagerDebugInfo {
296
296
  measuredCount: number
297
297
  itemLength: number
298
298
  coveragePercent: number
299
- estimatedHeight: number
299
+ itemHeight: number
300
300
  }
301
301
  ```
302
302
 
@@ -10,7 +10,7 @@ import type { HeightChange, ListManagerConfig, ListManagerDebugInfo } from './ty
10
10
  *
11
11
  * @example
12
12
  * ```typescript
13
- * const manager = new ReactiveListManager({ itemLength: 10000, estimatedHeight: 40 })
13
+ * const manager = new ReactiveListManager({ itemLength: 10000, itemHeight: 40 })
14
14
  *
15
15
  * // Process height changes incrementally
16
16
  * manager.processDirtyHeights(dirtyResults)
@@ -10,7 +10,7 @@ import { RecomputeScheduler } from './RecomputeScheduler.js';
10
10
  *
11
11
  * @example
12
12
  * ```typescript
13
- * const manager = new ReactiveListManager({ itemLength: 10000, estimatedHeight: 40 })
13
+ * const manager = new ReactiveListManager({ itemLength: 10000, itemHeight: 40 })
14
14
  *
15
15
  * // Process height changes incrementally
16
16
  * manager.processDirtyHeights(dirtyResults)
@@ -23,7 +23,7 @@
23
23
  * manager.processDirtyHeights(heightChanges)
24
24
  *
25
25
  * // Get reactive total height
26
- * const totalHeight = manager.getDerivedTotalHeight(calculatedItemHeight)
26
+ * const totalHeight = manager.totalHeight
27
27
  * ```
28
28
  *
29
29
  * @example Performance Monitoring
@@ -23,7 +23,7 @@
23
23
  * manager.processDirtyHeights(heightChanges)
24
24
  *
25
25
  * // Get reactive total height
26
- * const totalHeight = manager.getDerivedTotalHeight(calculatedItemHeight)
26
+ * const totalHeight = manager.totalHeight
27
27
  * ```
28
28
  *
29
29
  * @example Performance Monitoring
@@ -35,7 +35,7 @@ import type { SvelteVirtualListMode } from '../types.js';
35
35
  * calculateAverageHeightDebounced(
36
36
  * false,
37
37
  * null,
38
- * () => getVisibleRange(),
38
+ * visibleRange,
39
39
  * itemElements,
40
40
  * heightCache,
41
41
  * lastMeasuredIndex,
@@ -59,7 +59,7 @@ import type { SvelteVirtualListMode } from '../types.js';
59
59
  *
60
60
  * @param isCalculatingHeight - Flag to prevent concurrent calculations
61
61
  * @param heightUpdateTimeout - Reference to existing update timeout
62
- * @param visibleItemsGetter - Function to get current visible range
62
+ * @param visibleItems - Current visible range
63
63
  * @param itemElements - Array of DOM elements to measure
64
64
  * @param heightCache - Cache of previously measured heights with dirty tracking
65
65
  * @param lastMeasuredIndex - Index of last measured element
@@ -68,7 +68,7 @@ import type { SvelteVirtualListMode } from '../types.js';
68
68
  * @param debounceTime - Time to wait between calculations (default: 200ms)
69
69
  * @returns Timeout object or null if calculation was skipped
70
70
  */
71
- export declare const calculateAverageHeightDebounced: (isCalculatingHeight: boolean, heightUpdateTimeout: ReturnType<typeof setTimeout> | null, visibleItemsGetter: () => {
71
+ export declare const calculateAverageHeightDebounced: (isCalculatingHeight: boolean, heightUpdateTimeout: ReturnType<typeof setTimeout> | null, visibleItems: {
72
72
  start: number;
73
73
  end: number;
74
74
  }, itemElements: HTMLElement[], heightCache: Record<number, number>, lastMeasuredIndex: number, calculatedItemHeight: number, onUpdate: (result: {
@@ -36,7 +36,7 @@ import { BROWSER } from 'esm-env';
36
36
  * calculateAverageHeightDebounced(
37
37
  * false,
38
38
  * null,
39
- * () => getVisibleRange(),
39
+ * visibleRange,
40
40
  * itemElements,
41
41
  * heightCache,
42
42
  * lastMeasuredIndex,
@@ -60,7 +60,7 @@ import { BROWSER } from 'esm-env';
60
60
  *
61
61
  * @param isCalculatingHeight - Flag to prevent concurrent calculations
62
62
  * @param heightUpdateTimeout - Reference to existing update timeout
63
- * @param visibleItemsGetter - Function to get current visible range
63
+ * @param visibleItems - Current visible range
64
64
  * @param itemElements - Array of DOM elements to measure
65
65
  * @param heightCache - Cache of previously measured heights with dirty tracking
66
66
  * @param lastMeasuredIndex - Index of last measured element
@@ -69,19 +69,18 @@ import { BROWSER } from 'esm-env';
69
69
  * @param debounceTime - Time to wait between calculations (default: 200ms)
70
70
  * @returns Timeout object or null if calculation was skipped
71
71
  */
72
- export const calculateAverageHeightDebounced = (isCalculatingHeight, heightUpdateTimeout, visibleItemsGetter, itemElements, heightCache, lastMeasuredIndex, calculatedItemHeight,
72
+ export const calculateAverageHeightDebounced = (isCalculatingHeight, heightUpdateTimeout, visibleItems, itemElements, heightCache, lastMeasuredIndex, calculatedItemHeight,
73
73
  /* trunk-ignore(eslint/no-unused-vars) */
74
74
  onUpdate, debounceTime, dirtyItems, currentTotalHeight = 0, currentValidCount = 0, mode = 'topToBottom') => {
75
75
  if (!BROWSER || isCalculatingHeight)
76
76
  return null;
77
- const visibleRange = visibleItemsGetter();
78
- const currentIndex = visibleRange.start;
77
+ const currentIndex = visibleItems.start;
79
78
  if (currentIndex === lastMeasuredIndex && dirtyItems.size === 0)
80
79
  return null;
81
80
  if (heightUpdateTimeout)
82
81
  clearTimeout(heightUpdateTimeout);
83
82
  return setTimeout(() => {
84
- const { newHeight, newLastMeasuredIndex, updatedHeightCache, clearedDirtyItems, newTotalHeight, newValidCount, heightChanges } = calculateAverageHeight(itemElements, visibleRange, heightCache, calculatedItemHeight, dirtyItems, currentTotalHeight, currentValidCount, mode);
83
+ const { newHeight, newLastMeasuredIndex, updatedHeightCache, clearedDirtyItems, newTotalHeight, newValidCount, heightChanges } = calculateAverageHeight(itemElements, visibleItems, heightCache, calculatedItemHeight, dirtyItems, currentTotalHeight, currentValidCount, mode);
85
84
  if (Math.abs(newHeight - calculatedItemHeight) > 1 || dirtyItems.size > 0) {
86
85
  onUpdate({
87
86
  newHeight,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanspeak/svelte-virtual-list",
3
- "version": "0.3.13",
3
+ "version": "0.4.0",
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",