@humanspeak/svelte-virtual-list 0.2.6-beta.6 → 0.2.6-beta.7

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.
@@ -0,0 +1,200 @@
1
+ /**
2
+ * ReactiveHeightManager - A standalone reactive height calculation system
3
+ *
4
+ * Efficiently manages height calculations for virtualized lists by:
5
+ * - Tracking measured vs unmeasured items incrementally
6
+ * - Processing only dirty/changed items (O(dirty) instead of O(all))
7
+ * - Providing reactive state updates using Svelte 5 runes
8
+ * - Maintaining accurate total height calculations
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const manager = new ReactiveHeightManager({ itemLength: 10000, estimatedHeight: 40 })
13
+ *
14
+ * // Process height changes incrementally
15
+ * manager.processDirtyHeights(dirtyResults)
16
+ *
17
+ * // Update calculated item height
18
+ * manager.calculatedItemHeight = 42
19
+ *
20
+ * // Get reactive total height (automatically updates)
21
+ * const totalHeight = manager.totalHeight
22
+ * ```
23
+ */
24
+ export class ReactiveHeightManager {
25
+ // Reactive state using Svelte 5 runes
26
+ _totalMeasuredHeight = $state(0);
27
+ _measuredCount = $state(0);
28
+ _itemLength = $state(0);
29
+ _itemHeight = $state(40);
30
+ _averageHeight = $state(40);
31
+ _totalHeight = $state(0);
32
+ _measuredFlags = null;
33
+ recomputeDerivedHeights() {
34
+ const average = this._measuredCount > 0
35
+ ? this._totalMeasuredHeight / this._measuredCount
36
+ : this._itemHeight;
37
+ this._averageHeight = average;
38
+ const unmeasuredCount = this._itemLength - this._measuredCount;
39
+ this._totalHeight = this._totalMeasuredHeight + unmeasuredCount * average;
40
+ }
41
+ /**
42
+ * Get total measured height of all measured items
43
+ */
44
+ get totalMeasuredHeight() {
45
+ return this._totalMeasuredHeight;
46
+ }
47
+ /**
48
+ * Get count of items that have been measured
49
+ */
50
+ get measuredCount() {
51
+ return this._measuredCount;
52
+ }
53
+ /**
54
+ * Get total number of items in the list
55
+ */
56
+ get itemLength() {
57
+ return this._itemLength;
58
+ }
59
+ /**
60
+ * Get/Set the height to use for unmeasured items (reactive)
61
+ */
62
+ get itemHeight() {
63
+ return this._itemHeight;
64
+ }
65
+ set itemHeight(value) {
66
+ this._itemHeight = value;
67
+ this.recomputeDerivedHeights();
68
+ }
69
+ /**
70
+ * Get the calculated average height of measured items
71
+ * Falls back to itemHeight if no items have been measured yet
72
+ */
73
+ get averageHeight() {
74
+ return this._averageHeight;
75
+ }
76
+ /**
77
+ * Get the reactive total height of all items (measured + estimated)
78
+ * This automatically updates when any dependencies change
79
+ */
80
+ get totalHeight() {
81
+ return this._totalHeight;
82
+ }
83
+ /**
84
+ * Create a new ReactiveHeightManager instance
85
+ *
86
+ * @param config - Configuration object containing itemLength and itemHeight
87
+ */
88
+ constructor(config) {
89
+ this._itemLength = config.itemLength;
90
+ this._itemHeight = config.itemHeight;
91
+ this._measuredFlags = new Uint8Array(Math.max(0, this._itemLength));
92
+ this.recomputeDerivedHeights();
93
+ }
94
+ /**
95
+ * Process height changes incrementally - O(dirty items) instead of O(all items)
96
+ *
97
+ * This is the core optimization: instead of recalculating totals for all items,
98
+ * we only process the items that have changed, maintaining running totals.
99
+ *
100
+ * Accepts any object that has index, oldHeight, and newHeight properties,
101
+ * allowing consumers to pass objects with additional fields.
102
+ *
103
+ * @param dirtyResults - Array of height changes to process
104
+ */
105
+ processDirtyHeights(dirtyResults) {
106
+ if (dirtyResults.length === 0)
107
+ return;
108
+ // Batch calculate changes to trigger reactivity only once
109
+ let heightDelta = 0;
110
+ let countDelta = 0;
111
+ for (const change of dirtyResults) {
112
+ const { index, oldHeight, newHeight } = change;
113
+ // Remove old contribution if it existed
114
+ if (oldHeight !== undefined) {
115
+ heightDelta -= oldHeight;
116
+ countDelta -= 1;
117
+ }
118
+ // Add new contribution
119
+ if (newHeight !== undefined) {
120
+ heightDelta += newHeight;
121
+ countDelta += 1;
122
+ }
123
+ // Track measured flag (best-effort; full coalescing handled separately)
124
+ if (this._measuredFlags && index >= 0 && index < this._measuredFlags.length) {
125
+ this._measuredFlags[index] = 1;
126
+ }
127
+ }
128
+ if (heightDelta === 0 && countDelta === 0)
129
+ return;
130
+ // Apply all changes at once - triggers reactivity only once
131
+ this._totalMeasuredHeight += heightDelta;
132
+ this._measuredCount += countDelta;
133
+ this.recomputeDerivedHeights();
134
+ }
135
+ /**
136
+ * Update when items array length changes
137
+ *
138
+ * @param newLength - New total number of items
139
+ */
140
+ updateItemLength(newLength) {
141
+ this._itemLength = newLength;
142
+ this._measuredFlags = new Uint8Array(Math.max(0, newLength));
143
+ this.recomputeDerivedHeights();
144
+ }
145
+ /**
146
+ * Update estimated height for unmeasured items
147
+ *
148
+ * @param newEstimatedHeight - New estimated height
149
+ */
150
+ updateEstimatedHeight(newEstimatedHeight) {
151
+ // Keep a single source of truth for the estimated height
152
+ this._itemHeight = newEstimatedHeight;
153
+ this.recomputeDerivedHeights();
154
+ }
155
+ /**
156
+ * Reset all state to initial values
157
+ *
158
+ * Useful for testing or when completely reinitializing the list
159
+ */
160
+ reset() {
161
+ this._totalMeasuredHeight = 0;
162
+ this._measuredCount = 0;
163
+ this._measuredFlags = this._itemLength > 0 ? new Uint8Array(this._itemLength) : null;
164
+ // Note: Don't reset _itemLength, _itemHeight as they represent configuration, not measured state
165
+ this.recomputeDerivedHeights();
166
+ }
167
+ /**
168
+ * Get comprehensive debug information
169
+ *
170
+ * @returns Debug information object
171
+ */
172
+ getDebugInfo() {
173
+ return {
174
+ totalMeasuredHeight: this._totalMeasuredHeight,
175
+ measuredCount: this._measuredCount,
176
+ itemLength: this._itemLength,
177
+ coveragePercent: this._itemLength > 0 ? (this._measuredCount / this._itemLength) * 100 : 0,
178
+ itemHeight: this._itemHeight,
179
+ averageHeight: this.averageHeight,
180
+ totalHeight: this.totalHeight
181
+ };
182
+ }
183
+ /**
184
+ * Get the percentage of items that have been measured
185
+ *
186
+ * @returns Percentage (0-100) of measured items
187
+ */
188
+ getMeasurementCoverage() {
189
+ return this.getDebugInfo().coveragePercent;
190
+ }
191
+ /**
192
+ * Check if the manager has sufficient measurement data
193
+ *
194
+ * @param threshold - Minimum percentage of items that should be measured (default: 10)
195
+ * @returns true if coverage meets threshold
196
+ */
197
+ hasSufficientMeasurements(threshold = 10) {
198
+ return this.getMeasurementCoverage() >= threshold;
199
+ }
200
+ }
@@ -0,0 +1,5 @@
1
+ export declare function benchmarkHeightManager(itemCount: number, dirtyCount: number, iterations?: number): {
2
+ avgTime: number;
3
+ totalTime: number;
4
+ opsPerSecond: number;
5
+ };
@@ -0,0 +1,25 @@
1
+ import { createHeightManager } from './index.js';
2
+ export function benchmarkHeightManager(itemCount, dirtyCount, iterations = 100) {
3
+ const manager = createHeightManager(itemCount);
4
+ const times = [];
5
+ for (let i = 0; i < iterations; i++) {
6
+ const dirtyResults = Array.from({ length: dirtyCount }, (_, idx) => ({
7
+ index: idx,
8
+ oldHeight: undefined,
9
+ newHeight: 40 + Math.random() * 20
10
+ }));
11
+ const start = performance.now();
12
+ manager.processDirtyHeights(dirtyResults);
13
+ const end = performance.now();
14
+ times.push(end - start);
15
+ manager.reset(); // Reset for next iteration
16
+ }
17
+ const totalTime = times.reduce((sum, time) => sum + time, 0);
18
+ const avgTime = totalTime / iterations;
19
+ const opsPerSecond = 1000 / avgTime; // Convert ms to ops/second
20
+ return {
21
+ avgTime,
22
+ totalTime,
23
+ opsPerSecond
24
+ };
25
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Reactive Height Manager
3
+ *
4
+ * A standalone, high-performance reactive height calculation system for virtualized lists.
5
+ *
6
+ * Features:
7
+ * - Incremental height processing (O(dirty items) instead of O(all items))
8
+ * - Reactive state management using Svelte 5 runes
9
+ * - Comprehensive performance testing
10
+ * - Framework-agnostic types and interfaces
11
+ * - Memory-efficient measurement tracking
12
+ *
13
+ * @example Basic Usage
14
+ * ```typescript
15
+ * import { ReactiveHeightManager } from './reactive-height-manager'
16
+ *
17
+ * const manager = new ReactiveHeightManager({
18
+ * itemLength: 10000,
19
+ * estimatedHeight: 40
20
+ * })
21
+ *
22
+ * // Process height changes incrementally
23
+ * manager.processDirtyHeights(heightChanges)
24
+ *
25
+ * // Get reactive total height
26
+ * const totalHeight = manager.getDerivedTotalHeight(calculatedItemHeight)
27
+ * ```
28
+ *
29
+ * @example Performance Monitoring
30
+ * ```typescript
31
+ * const debugInfo = manager.getDebugInfo()
32
+ * console.log(`Coverage: ${debugInfo.coveragePercent}%`)
33
+ * console.log(`Measured: ${debugInfo.measuredCount}/${debugInfo.itemLength}`)
34
+ * ```
35
+ */
36
+ export { ReactiveHeightManager } from './ReactiveHeightManager.svelte.js';
37
+ import { ReactiveHeightManager as ReactiveHeightManagerType } from './ReactiveHeightManager.svelte.js';
38
+ export type { HeightChange, HeightManagerConfig, HeightManagerDebugInfo } from './types.js';
39
+ export declare const VERSION = "1.0.0";
40
+ export declare const DEFAULT_ESTIMATED_HEIGHT = 40;
41
+ export declare const DEFAULT_MEASUREMENT_THRESHOLD = 10;
42
+ /**
43
+ * Factory function for creating ReactiveHeightManager instances
44
+ * with common configurations
45
+ */
46
+ export declare function createHeightManager(itemLength: number, itemHeight?: number): ReactiveHeightManagerType;
47
+ /**
48
+ * Performance benchmarking utility
49
+ */
50
+ export { benchmarkHeightManager } from './benchmark.js';
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Reactive Height Manager
3
+ *
4
+ * A standalone, high-performance reactive height calculation system for virtualized lists.
5
+ *
6
+ * Features:
7
+ * - Incremental height processing (O(dirty items) instead of O(all items))
8
+ * - Reactive state management using Svelte 5 runes
9
+ * - Comprehensive performance testing
10
+ * - Framework-agnostic types and interfaces
11
+ * - Memory-efficient measurement tracking
12
+ *
13
+ * @example Basic Usage
14
+ * ```typescript
15
+ * import { ReactiveHeightManager } from './reactive-height-manager'
16
+ *
17
+ * const manager = new ReactiveHeightManager({
18
+ * itemLength: 10000,
19
+ * estimatedHeight: 40
20
+ * })
21
+ *
22
+ * // Process height changes incrementally
23
+ * manager.processDirtyHeights(heightChanges)
24
+ *
25
+ * // Get reactive total height
26
+ * const totalHeight = manager.getDerivedTotalHeight(calculatedItemHeight)
27
+ * ```
28
+ *
29
+ * @example Performance Monitoring
30
+ * ```typescript
31
+ * const debugInfo = manager.getDebugInfo()
32
+ * console.log(`Coverage: ${debugInfo.coveragePercent}%`)
33
+ * console.log(`Measured: ${debugInfo.measuredCount}/${debugInfo.itemLength}`)
34
+ * ```
35
+ */
36
+ // Export the main class
37
+ export { ReactiveHeightManager } from './ReactiveHeightManager.svelte.js';
38
+ import { ReactiveHeightManager as ReactiveHeightManagerType } from './ReactiveHeightManager.svelte.js';
39
+ // Export version for potential npm package
40
+ export const VERSION = '1.0.0';
41
+ // Export utility constants
42
+ export const DEFAULT_ESTIMATED_HEIGHT = 40;
43
+ export const DEFAULT_MEASUREMENT_THRESHOLD = 10; // percentage
44
+ /**
45
+ * Factory function for creating ReactiveHeightManager instances
46
+ * with common configurations
47
+ */
48
+ export function createHeightManager(itemLength, itemHeight = DEFAULT_ESTIMATED_HEIGHT) {
49
+ return new ReactiveHeightManagerType({ itemLength, itemHeight });
50
+ }
51
+ /**
52
+ * Performance benchmarking utility
53
+ */
54
+ // Moved out to keep index clean; re-exported from benchmark.ts
55
+ export { benchmarkHeightManager } from './benchmark.js';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Represents a height change for a specific item
3
+ * This interface accepts any object with these minimum properties,
4
+ * allowing for additional fields that consumers may include
5
+ */
6
+ export interface HeightChange {
7
+ /** The index of the item that changed */
8
+ readonly index: number;
9
+ /** The previous height (undefined if first measurement) */
10
+ readonly oldHeight: number | undefined;
11
+ /** The new height measurement */
12
+ readonly newHeight: number;
13
+ }
14
+ /**
15
+ * Configuration options for ReactiveHeightManager
16
+ */
17
+ export interface HeightManagerConfig {
18
+ /** Total number of items in the list */
19
+ itemLength: number;
20
+ /** Height to use for unmeasured items */
21
+ itemHeight: number;
22
+ }
23
+ /**
24
+ * Debug information about the height manager state
25
+ */
26
+ export interface HeightManagerDebugInfo {
27
+ /** Total measured height of all measured items */
28
+ totalMeasuredHeight: number;
29
+ /** Number of items that have been measured */
30
+ measuredCount: number;
31
+ /** Total number of items in the list */
32
+ itemLength: number;
33
+ /** Percentage of items that have been measured */
34
+ coveragePercent: number;
35
+ /** Current height to use for unmeasured items */
36
+ itemHeight: number;
37
+ /** Calculated average height of measured items */
38
+ averageHeight: number;
39
+ /** Current total height (measured + estimated) */
40
+ totalHeight: number;
41
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,4 @@
1
+ import type { SvelteVirtualListMode } from '../types.js';
1
2
  /**
2
3
  * Calculates and updates the average height of visible items with debouncing.
3
4
  *
@@ -77,4 +78,10 @@ export declare const calculateAverageHeightDebounced: (isCalculatingHeight: bool
77
78
  clearedDirtyItems: Set<number>;
78
79
  newTotalHeight: number;
79
80
  newValidCount: number;
80
- }) => void, debounceTime: number, dirtyItems: Set<number>, currentTotalHeight?: number, currentValidCount?: number) => NodeJS.Timeout | null;
81
+ heightChanges: Array<{
82
+ index: number;
83
+ oldHeight: number;
84
+ newHeight: number;
85
+ delta: number;
86
+ }>;
87
+ }) => void, debounceTime: number, dirtyItems: Set<number>, currentTotalHeight?: number, currentValidCount?: number, mode?: SvelteVirtualListMode) => NodeJS.Timeout | null;
@@ -71,17 +71,17 @@ import { BROWSER } from 'esm-env';
71
71
  */
72
72
  export const calculateAverageHeightDebounced = (isCalculatingHeight, heightUpdateTimeout, visibleItemsGetter, itemElements, heightCache, lastMeasuredIndex, calculatedItemHeight,
73
73
  /* trunk-ignore(eslint/no-unused-vars) */
74
- onUpdate, debounceTime, dirtyItems, currentTotalHeight = 0, currentValidCount = 0) => {
74
+ onUpdate, debounceTime, dirtyItems, currentTotalHeight = 0, currentValidCount = 0, mode = 'topToBottom') => {
75
75
  if (!BROWSER || isCalculatingHeight)
76
76
  return null;
77
77
  const visibleRange = visibleItemsGetter();
78
78
  const currentIndex = visibleRange.start;
79
- if (currentIndex === lastMeasuredIndex)
79
+ if (currentIndex === lastMeasuredIndex && dirtyItems.size === 0)
80
80
  return null;
81
81
  if (heightUpdateTimeout)
82
82
  clearTimeout(heightUpdateTimeout);
83
83
  return setTimeout(() => {
84
- const { newHeight, newLastMeasuredIndex, updatedHeightCache, clearedDirtyItems, newTotalHeight, newValidCount } = calculateAverageHeight(itemElements, visibleRange, heightCache, calculatedItemHeight, dirtyItems, currentTotalHeight, currentValidCount);
84
+ const { newHeight, newLastMeasuredIndex, updatedHeightCache, clearedDirtyItems, newTotalHeight, newValidCount, heightChanges } = calculateAverageHeight(itemElements, visibleRange, heightCache, calculatedItemHeight, dirtyItems, currentTotalHeight, currentValidCount, mode);
85
85
  if (Math.abs(newHeight - calculatedItemHeight) > 1 || dirtyItems.size > 0) {
86
86
  onUpdate({
87
87
  newHeight,
@@ -89,7 +89,8 @@ onUpdate, debounceTime, dirtyItems, currentTotalHeight = 0, currentValidCount =
89
89
  updatedHeightCache,
90
90
  clearedDirtyItems,
91
91
  newTotalHeight,
92
- newValidCount
92
+ newValidCount,
93
+ heightChanges
93
94
  });
94
95
  }
95
96
  }, debounceTime);
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Utility functions for detecting significant height changes in virtual list items
3
+ */
4
+ /**
5
+ * Checks if a height change is significant enough to warrant marking an item as dirty
6
+ * @param itemIndex - The index of the item
7
+ * @param newHeight - The new measured height
8
+ * @param heightCache - Existing height cache to compare against
9
+ * @param marginOfError - Height difference threshold (default: 1px)
10
+ * @returns true if the height change is significant
11
+ */
12
+ export declare const isSignificantHeightChange: (itemIndex: number, newHeight: number, heightCache: Record<number, number>, marginOfError?: number) => boolean;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Utility functions for detecting significant height changes in virtual list items
3
+ */
4
+ /**
5
+ * Checks if a height change is significant enough to warrant marking an item as dirty
6
+ * @param itemIndex - The index of the item
7
+ * @param newHeight - The new measured height
8
+ * @param heightCache - Existing height cache to compare against
9
+ * @param marginOfError - Height difference threshold (default: 1px)
10
+ * @returns true if the height change is significant
11
+ */
12
+ export const isSignificantHeightChange = (itemIndex, newHeight, heightCache, marginOfError = 1) => {
13
+ const previousHeight = heightCache[itemIndex];
14
+ if (previousHeight === undefined) {
15
+ // First time seeing this item, mark as significant
16
+ return true;
17
+ }
18
+ const heightDifference = Math.abs(newHeight - previousHeight);
19
+ return heightDifference > marginOfError;
20
+ };
@@ -7,39 +7,6 @@ export interface ItemResizeConfig {
7
7
  /** Callback when items are marked as dirty */
8
8
  onItemsDirty?: (dirtyIndices: Set<number>) => void;
9
9
  }
10
- /**
11
- * Creates a ResizeObserver for monitoring individual item size changes.
12
- *
13
- * This function creates a ResizeObserver that watches for size changes in list items
14
- * and maintains a dirty set of items that need height recalculation. It's designed
15
- * specifically for virtual list components where item heights may change dynamically.
16
- *
17
- * @param itemElements - Array of item elements to watch
18
- * @param getVisibleRange - Function to get current visible range
19
- * @param dirtyItems - Set to track items that need recalculation
20
- * @param config - Configuration options
21
- * @returns ResizeObserver instance
22
- *
23
- * @example
24
- * ```typescript
25
- * const itemElements = $state<HTMLElement[]>([])
26
- * const dirtyItems = $state(new Set<number>())
27
- *
28
- * const resizeObserver = createItemResizeObserver(
29
- * itemElements,
30
- * () => ({ start: 0, end: 10 }),
31
- * dirtyItems,
32
- * {
33
- * debug: true,
34
- * onItemsDirty: (indices) => console.log('Items dirty:', indices)
35
- * }
36
- * )
37
- * ```
38
- */
39
- export declare const createItemResizeObserver: (itemElements: HTMLElement[], getVisibleRange: () => {
40
- start: number;
41
- end: number;
42
- }, dirtyItems: Set<number>, config?: ItemResizeConfig) => ResizeObserver;
43
10
  /**
44
11
  * Configuration for container resize observation
45
12
  */
@@ -1,60 +1,3 @@
1
- /**
2
- * Creates a ResizeObserver for monitoring individual item size changes.
3
- *
4
- * This function creates a ResizeObserver that watches for size changes in list items
5
- * and maintains a dirty set of items that need height recalculation. It's designed
6
- * specifically for virtual list components where item heights may change dynamically.
7
- *
8
- * @param itemElements - Array of item elements to watch
9
- * @param getVisibleRange - Function to get current visible range
10
- * @param dirtyItems - Set to track items that need recalculation
11
- * @param config - Configuration options
12
- * @returns ResizeObserver instance
13
- *
14
- * @example
15
- * ```typescript
16
- * const itemElements = $state<HTMLElement[]>([])
17
- * const dirtyItems = $state(new Set<number>())
18
- *
19
- * const resizeObserver = createItemResizeObserver(
20
- * itemElements,
21
- * () => ({ start: 0, end: 10 }),
22
- * dirtyItems,
23
- * {
24
- * debug: true,
25
- * onItemsDirty: (indices) => console.log('Items dirty:', indices)
26
- * }
27
- * )
28
- * ```
29
- */
30
- export const createItemResizeObserver = (itemElements, getVisibleRange, dirtyItems, config = {}) => {
31
- const { debug = false, onItemsDirty } = config;
32
- return new ResizeObserver((entries) => {
33
- let shouldRecalculate = false;
34
- const newDirtyItems = new Set();
35
- if (debug) {
36
- console.log(`ResizeObserver fired for ${entries.length} entries`);
37
- }
38
- for (const entry of entries) {
39
- const element = entry.target;
40
- const elementIndex = itemElements.indexOf(element);
41
- if (elementIndex !== -1) {
42
- const visibleRange = getVisibleRange();
43
- const actualIndex = visibleRange.start + elementIndex;
44
- // ResizeObserver fired = element resized, so add to dirty queue
45
- dirtyItems.add(actualIndex);
46
- newDirtyItems.add(actualIndex);
47
- shouldRecalculate = true;
48
- if (debug) {
49
- console.log(`Item ${actualIndex} marked dirty (resized), queue size: ${dirtyItems.size}`);
50
- }
51
- }
52
- }
53
- if (shouldRecalculate && onItemsDirty) {
54
- onItemsDirty(newDirtyItems);
55
- }
56
- });
57
- };
58
1
  /**
59
2
  * Creates a ResizeObserver for monitoring container size changes.
60
3
  *
@@ -1,10 +1,10 @@
1
- import type { SvelteVirtualListMode, SvelteVirtualListScrollAlignment } from '../types.js';
1
+ import type { SvelteVirtualListMode, SvelteVirtualListScrollAlign } from '../types.js';
2
2
  /**
3
3
  * Parameters for calculating scroll target position
4
4
  */
5
5
  export interface ScrollTargetParams {
6
6
  mode: SvelteVirtualListMode;
7
- align: SvelteVirtualListScrollAlignment;
7
+ align: SvelteVirtualListScrollAlign;
8
8
  targetIndex: number;
9
9
  itemsLength: number;
10
10
  calculatedItemHeight: number;