@humanspeak/svelte-virtual-list 0.2.6 → 0.3.1-beta.1

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.
Files changed (32) hide show
  1. package/README.md +50 -13
  2. package/dist/SvelteVirtualList.svelte +619 -179
  3. package/dist/SvelteVirtualList.svelte.d.ts +156 -65
  4. package/dist/reactive-height-manager/INTEGRATION_EXAMPLE.md +136 -0
  5. package/dist/reactive-height-manager/README.md +324 -0
  6. package/dist/reactive-height-manager/ReactiveHeightManager.svelte.d.ts +116 -0
  7. package/dist/reactive-height-manager/ReactiveHeightManager.svelte.js +200 -0
  8. package/dist/reactive-height-manager/benchmark.d.ts +5 -0
  9. package/dist/reactive-height-manager/benchmark.js +25 -0
  10. package/dist/reactive-height-manager/index.d.ts +50 -0
  11. package/dist/reactive-height-manager/index.js +55 -0
  12. package/dist/reactive-height-manager/test/TestComponent.svelte +78 -0
  13. package/dist/reactive-height-manager/test/TestComponent.svelte.d.ts +23 -0
  14. package/dist/reactive-height-manager/types.d.ts +41 -0
  15. package/dist/reactive-height-manager/types.js +1 -0
  16. package/dist/types.d.ts +24 -5
  17. package/dist/utils/heightCalculation.d.ts +18 -8
  18. package/dist/utils/heightCalculation.js +18 -11
  19. package/dist/utils/heightChangeDetection.d.ts +12 -0
  20. package/dist/utils/heightChangeDetection.js +20 -0
  21. package/dist/utils/resizeObserver.d.ts +89 -0
  22. package/dist/utils/resizeObserver.js +119 -0
  23. package/dist/utils/scrollCalculation.d.ts +47 -0
  24. package/dist/utils/scrollCalculation.js +167 -0
  25. package/dist/utils/throttle.d.ts +95 -0
  26. package/dist/utils/throttle.js +155 -0
  27. package/dist/utils/types.d.ts +0 -6
  28. package/dist/utils/virtualList.d.ts +20 -23
  29. package/dist/utils/virtualList.js +153 -61
  30. package/dist/utils/virtualListDebug.d.ts +12 -7
  31. package/dist/utils/virtualListDebug.js +19 -9
  32. package/package.json +33 -31
@@ -0,0 +1,324 @@
1
+ # ReactiveHeightManager
2
+
3
+ > A standalone, high-performance reactive height calculation system for virtualized lists
4
+
5
+ <!-- [![Tests](https://img.shields.io/badge/tests-13%20passing-brightgreen)](./ReactiveHeightManager.test.ts)
6
+ [![Performance](https://img.shields.io/badge/performance-1000%20updates%20%3C1ms-blue)](#performance)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/) -->
8
+
9
+ ## 🚀 Features
10
+
11
+ - **Incremental Processing**: O(dirty items) instead of O(all items)
12
+ - **Reactive State**: Built with Svelte 5 runes for automatic updates
13
+ - **Framework Agnostic**: Standalone types, no external dependencies
14
+ - **High Performance**: 1000+ height updates processed in <1ms
15
+ - **Memory Efficient**: Tracks only measured vs estimated items
16
+ - **Comprehensive Testing**: 13 test cases including performance benchmarks
17
+
18
+ ## 🎯 Problem It Solves
19
+
20
+ Traditional virtual list height calculations loop through **all items** on every change:
21
+
22
+ ```typescript
23
+ // ❌ O(n) - Slow for large lists
24
+ let totalHeight = 0
25
+ for (let i = 0; i < items.length; i++) {
26
+ totalHeight += heightCache[i] || estimatedHeight
27
+ }
28
+ ```
29
+
30
+ ReactiveHeightManager processes only **dirty/changed items**:
31
+
32
+ ```typescript
33
+ // ✅ O(dirty items) - Fast and reactive
34
+ manager.processDirtyHeights(changedItems)
35
+ const totalHeight = manager.getDerivedTotalHeight()
36
+ ```
37
+
38
+ ## 📦 Installation
39
+
40
+ ```bash
41
+ # If using within svelte-virtual-list project
42
+ import { ReactiveHeightManager } from '$lib/reactive-height-manager'
43
+
44
+ # For standalone usage (copy the module)
45
+ cp -r src/lib/reactive-height-manager your-project/src/lib/
46
+ ```
47
+
48
+ ## 🚀 Quick Start
49
+
50
+ ```typescript
51
+ import { ReactiveHeightManager } from './reactive-height-manager'
52
+
53
+ // Create manager
54
+ const manager = new ReactiveHeightManager({
55
+ itemLength: 10000,
56
+ itemHeight: 40
57
+ })
58
+
59
+ // Process height changes
60
+ const heightChanges = [
61
+ { index: 0, oldHeight: undefined, newHeight: 45 },
62
+ { index: 1, oldHeight: 40, newHeight: 50 }
63
+ ]
64
+ manager.processDirtyHeights(heightChanges)
65
+
66
+ // Update calculated item height
67
+ manager.calculatedItemHeight = 42
68
+
69
+ // Get reactive total height (automatically updates)
70
+ const totalHeight = manager.totalHeight
71
+ console.log(`Total height: ${totalHeight}px`)
72
+ ```
73
+
74
+ ## 📖 API Documentation
75
+
76
+ ### Constructor
77
+
78
+ ```typescript
79
+ new ReactiveHeightManager(config: HeightManagerConfig)
80
+ ```
81
+
82
+ **Parameters:**
83
+
84
+ - `config.itemLength` - Total number of items
85
+ - `config.estimatedHeight` - Default height for unmeasured items
86
+
87
+ ### Core Methods
88
+
89
+ #### `processDirtyHeights(changes: HeightChange[])`
90
+
91
+ Process height changes incrementally. This is the performance-critical method.
92
+
93
+ ```typescript
94
+ const changes = [{ index: 0, oldHeight: undefined, newHeight: 45 }]
95
+ manager.processDirtyHeights(changes)
96
+ ```
97
+
98
+ #### `totalHeight` (getter)
99
+
100
+ Get total height of all items (measured + estimated). This property is reactive and automatically updates when dependencies change.
101
+
102
+ ```typescript
103
+ const totalHeight = manager.totalHeight // Automatically reactive
104
+ ```
105
+
106
+ #### `calculatedItemHeight` (getter/setter)
107
+
108
+ Get or set the calculated average item height, which affects total height calculations.
109
+
110
+ ```typescript
111
+ manager.calculatedItemHeight = 42 // Updates totalHeight automatically
112
+ const currentHeight = manager.calculatedItemHeight
113
+ ```
114
+
115
+ ### State Management
116
+
117
+ #### `updateItemLength(newLength: number)`
118
+
119
+ Update when items array changes.
120
+
121
+ #### `updateEstimatedHeight(newHeight: number)`
122
+
123
+ Update estimated height for unmeasured items.
124
+
125
+ #### `reset()`
126
+
127
+ Reset all state to initial values.
128
+
129
+ ### Utilities
130
+
131
+ #### `getDebugInfo(): HeightManagerDebugInfo`
132
+
133
+ Get comprehensive debug information.
134
+
135
+ ```typescript
136
+ const debug = manager.getDebugInfo()
137
+ console.log(`Coverage: ${debug.coveragePercent}%`)
138
+ console.log(`Measured: ${debug.measuredCount}/${debug.itemLength}`)
139
+ ```
140
+
141
+ #### `getMeasurementCoverage(): number`
142
+
143
+ Get percentage of items measured (0-100).
144
+
145
+ #### `hasSufficientMeasurements(threshold?: number): boolean`
146
+
147
+ Check if manager has sufficient measurement data.
148
+
149
+ ## 🎨 Integration Examples
150
+
151
+ ### With SvelteVirtualList
152
+
153
+ ```typescript
154
+ // Create manager
155
+ const heightManager = new ReactiveHeightManager({
156
+ itemLength: items.length,
157
+ estimatedHeight: defaultEstimatedItemHeight
158
+ })
159
+
160
+ // Update on items change
161
+ $effect(() => {
162
+ heightManager.updateItemLength(items.length)
163
+ })
164
+
165
+ // Process in callback
166
+ const updateHeight = () => {
167
+ heightUpdateTimeout = calculateAverageHeightDebounced(
168
+ // ... params
169
+ (result) => {
170
+ // Convert types if needed (or pass directly if compatible)
171
+ const heightChanges = result.heightChanges
172
+
173
+ // Process incrementally
174
+ heightManager.processDirtyHeights(heightChanges)
175
+ }
176
+ )
177
+ }
178
+
179
+ // Update calculated height when needed
180
+ $effect(() => {
181
+ heightManager.calculatedItemHeight = calculatedItemHeight
182
+ })
183
+
184
+ // Reactive total height (automatically updates)
185
+ let totalHeight = $derived(() => heightManager.totalHeight)
186
+ ```
187
+
188
+ ### Standalone Usage
189
+
190
+ ```typescript
191
+ import { ReactiveHeightManager, benchmarkHeightManager } from './reactive-height-manager'
192
+
193
+ const manager = new ReactiveHeightManager({ itemLength: 1000, estimatedHeight: 50 })
194
+
195
+ // Performance monitoring
196
+ const results = benchmarkHeightManager(10000, 1000, 100)
197
+ console.log(`Average time: ${results.avgTime.toFixed(2)}ms`)
198
+ console.log(`Operations/sec: ${results.opsPerSecond.toFixed(0)}`)
199
+
200
+ // Test reactive totalHeight
201
+ manager.calculatedItemHeight = 50 // Triggers reactive update
202
+ console.log(`New total height: ${manager.totalHeight}`)
203
+ ```
204
+
205
+ ## ⚡ Performance
206
+
207
+ ### Benchmarks
208
+
209
+ | Operation | Items | Time | Performance |
210
+ | -------------------- | ------ | ------ | ------------ |
211
+ | 1,000 dirty updates | 10,000 | < 1ms | 🚀 Excellent |
212
+ | 10,000 dirty updates | 10,000 | < 10ms | 🚀 Excellent |
213
+ | Complex scenarios | 5,000 | < 25ms | ✅ Good |
214
+
215
+ ### Memory Usage
216
+
217
+ - **Measured Items**: Tracked incrementally
218
+ - **Unmeasured Items**: Single multiplier calculation
219
+ - **State**: Only 4 reactive variables total
220
+
221
+ ### Compared to O(n) Loop
222
+
223
+ ```typescript
224
+ // Before: O(n) calculation every time
225
+ for (let i = 0; i < 100000; i++) {
226
+ /* ... */
227
+ } // ~10-50ms
228
+
229
+ // After: O(1) reactive access
230
+ manager.totalHeight // ~0.01ms
231
+ ```
232
+
233
+ ## 🧪 Testing
234
+
235
+ ```bash
236
+ # Run all tests
237
+ npm run test -- ReactiveHeightManager.test.ts
238
+
239
+ # Verbose output
240
+ npm run test -- ReactiveHeightManager.test.ts --reporter=verbose
241
+
242
+ # Performance benchmarking
243
+ npm run test -- --grep "Performance Tests"
244
+ ```
245
+
246
+ ### Test Coverage
247
+
248
+ - ✅ **Initialization** (2 tests)
249
+ - ✅ **Performance** (3 tests) - Sub-millisecond operations
250
+ - ✅ **Accuracy** (2 tests) - Complex scenarios with alternating heights
251
+ - ✅ **State Management** (3 tests) - Updates, resets, configuration
252
+ - ✅ **Utilities** (3 tests) - Coverage tracking, debug info
253
+
254
+ ## 🏗️ Architecture
255
+
256
+ ### Core Principles
257
+
258
+ 1. **Reactive State**: Uses Svelte 5 `$state` runes
259
+ 2. **Incremental Processing**: Only process changed items
260
+ 3. **Memory Efficiency**: Track totals, not individual measurements
261
+ 4. **Type Safety**: Comprehensive TypeScript interfaces
262
+
263
+ ### Data Flow
264
+
265
+ ```text
266
+
267
+ Height Changes → processDirtyHeights() → Update State → getDerivedTotalHeight() → Reactive UI
268
+
269
+ ```
270
+
271
+ ### Internal State
272
+
273
+ ```typescript
274
+ private _totalMeasuredHeight = $state(0) // Sum of all measured heights
275
+ private _measuredCount = $state(0) // Count of measured items
276
+ private _itemLength = $state(0) // Total items
277
+ private _estimatedHeight = $state(40) // Default estimate
278
+ ```
279
+
280
+ ## 🔧 Types
281
+
282
+ ```typescript
283
+ interface HeightChange {
284
+ readonly index: number
285
+ readonly oldHeight: number | undefined
286
+ readonly newHeight: number
287
+ }
288
+
289
+ interface HeightManagerConfig {
290
+ itemLength: number
291
+ estimatedHeight: number
292
+ }
293
+
294
+ interface HeightManagerDebugInfo {
295
+ totalMeasuredHeight: number
296
+ measuredCount: number
297
+ itemLength: number
298
+ coveragePercent: number
299
+ estimatedHeight: number
300
+ }
301
+ ```
302
+
303
+ ## 📈 Roadmap
304
+
305
+ - [ ] Batch processing for very large change sets
306
+ - [ ] Configurable block-based calculations
307
+ - [ ] Export as standalone npm package
308
+ - [ ] React/Vue adapter examples
309
+ - [ ] Web Worker support for massive datasets
310
+
311
+ ## 🤝 Contributing
312
+
313
+ 1. Run tests: `npm run test -- ReactiveHeightManager.test.ts`
314
+ 2. Add performance benchmarks for new features
315
+ 3. Maintain O(1) or O(dirty) complexity
316
+ 4. Update type definitions
317
+
318
+ ## 📄 License
319
+
320
+ Part of the svelte-virtual-list project. See main [LICENSE](../../../LICENSE) file.
321
+
322
+ ---
323
+
324
+ **Need help?** Check the [Integration Examples](./INTEGRATION_EXAMPLE.md) for detailed usage patterns.
@@ -0,0 +1,116 @@
1
+ import type { HeightChange, HeightManagerConfig, HeightManagerDebugInfo } from './types.js';
2
+ /**
3
+ * ReactiveHeightManager - A standalone reactive height calculation system
4
+ *
5
+ * Efficiently manages height calculations for virtualized lists by:
6
+ * - Tracking measured vs unmeasured items incrementally
7
+ * - Processing only dirty/changed items (O(dirty) instead of O(all))
8
+ * - Providing reactive state updates using Svelte 5 runes
9
+ * - Maintaining accurate total height calculations
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const manager = new ReactiveHeightManager({ itemLength: 10000, estimatedHeight: 40 })
14
+ *
15
+ * // Process height changes incrementally
16
+ * manager.processDirtyHeights(dirtyResults)
17
+ *
18
+ * // Update calculated item height
19
+ * manager.calculatedItemHeight = 42
20
+ *
21
+ * // Get reactive total height (automatically updates)
22
+ * const totalHeight = manager.totalHeight
23
+ * ```
24
+ */
25
+ export declare class ReactiveHeightManager {
26
+ private _totalMeasuredHeight;
27
+ private _measuredCount;
28
+ private _itemLength;
29
+ private _itemHeight;
30
+ private _averageHeight;
31
+ private _totalHeight;
32
+ private _measuredFlags;
33
+ private recomputeDerivedHeights;
34
+ /**
35
+ * Get total measured height of all measured items
36
+ */
37
+ get totalMeasuredHeight(): number;
38
+ /**
39
+ * Get count of items that have been measured
40
+ */
41
+ get measuredCount(): number;
42
+ /**
43
+ * Get total number of items in the list
44
+ */
45
+ get itemLength(): number;
46
+ /**
47
+ * Get/Set the height to use for unmeasured items (reactive)
48
+ */
49
+ get itemHeight(): number;
50
+ set itemHeight(value: number);
51
+ /**
52
+ * Get the calculated average height of measured items
53
+ * Falls back to itemHeight if no items have been measured yet
54
+ */
55
+ get averageHeight(): number;
56
+ /**
57
+ * Get the reactive total height of all items (measured + estimated)
58
+ * This automatically updates when any dependencies change
59
+ */
60
+ get totalHeight(): number;
61
+ /**
62
+ * Create a new ReactiveHeightManager instance
63
+ *
64
+ * @param config - Configuration object containing itemLength and itemHeight
65
+ */
66
+ constructor(config: HeightManagerConfig);
67
+ /**
68
+ * Process height changes incrementally - O(dirty items) instead of O(all items)
69
+ *
70
+ * This is the core optimization: instead of recalculating totals for all items,
71
+ * we only process the items that have changed, maintaining running totals.
72
+ *
73
+ * Accepts any object that has index, oldHeight, and newHeight properties,
74
+ * allowing consumers to pass objects with additional fields.
75
+ *
76
+ * @param dirtyResults - Array of height changes to process
77
+ */
78
+ processDirtyHeights(dirtyResults: HeightChange[]): void;
79
+ /**
80
+ * Update when items array length changes
81
+ *
82
+ * @param newLength - New total number of items
83
+ */
84
+ updateItemLength(newLength: number): void;
85
+ /**
86
+ * Update estimated height for unmeasured items
87
+ *
88
+ * @param newEstimatedHeight - New estimated height
89
+ */
90
+ updateEstimatedHeight(newEstimatedHeight: number): void;
91
+ /**
92
+ * Reset all state to initial values
93
+ *
94
+ * Useful for testing or when completely reinitializing the list
95
+ */
96
+ reset(): void;
97
+ /**
98
+ * Get comprehensive debug information
99
+ *
100
+ * @returns Debug information object
101
+ */
102
+ getDebugInfo(): HeightManagerDebugInfo;
103
+ /**
104
+ * Get the percentage of items that have been measured
105
+ *
106
+ * @returns Percentage (0-100) of measured items
107
+ */
108
+ getMeasurementCoverage(): number;
109
+ /**
110
+ * Check if the manager has sufficient measurement data
111
+ *
112
+ * @param threshold - Minimum percentage of items that should be measured (default: 10)
113
+ * @returns true if coverage meets threshold
114
+ */
115
+ hasSufficientMeasurements(threshold?: number): boolean;
116
+ }
@@ -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
+ }