@humanspeak/svelte-virtual-list 0.3.1-beta.1 → 0.3.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 (26) hide show
  1. package/dist/SvelteVirtualList.svelte +262 -191
  2. package/dist/SvelteVirtualList.svelte.d.ts +5 -5
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.js +2 -0
  5. package/dist/{reactive-height-manager → reactive-list-manager}/INTEGRATION_EXAMPLE.md +10 -10
  6. package/dist/{reactive-height-manager → reactive-list-manager}/README.md +17 -17
  7. package/dist/reactive-list-manager/ReactiveListManager.svelte.d.ts +221 -0
  8. package/dist/reactive-list-manager/ReactiveListManager.svelte.js +635 -0
  9. package/dist/reactive-list-manager/RecomputeScheduler.d.ts +12 -0
  10. package/dist/reactive-list-manager/RecomputeScheduler.js +54 -0
  11. package/dist/reactive-list-manager/benchmark.d.ts +5 -0
  12. package/dist/{reactive-height-manager → reactive-list-manager}/benchmark.js +3 -3
  13. package/dist/{reactive-height-manager → reactive-list-manager}/index.d.ts +8 -12
  14. package/dist/{reactive-height-manager → reactive-list-manager}/index.js +10 -13
  15. package/dist/{reactive-height-manager → reactive-list-manager}/test/TestComponent.svelte +9 -9
  16. package/dist/{reactive-height-manager → reactive-list-manager}/test/TestComponent.svelte.d.ts +5 -5
  17. package/dist/{reactive-height-manager → reactive-list-manager}/types.d.ts +9 -3
  18. package/dist/utils/virtualList.d.ts +2 -2
  19. package/dist/utils/virtualList.js +44 -17
  20. package/package.json +134 -133
  21. package/dist/reactive-height-manager/ReactiveHeightManager.svelte.d.ts +0 -116
  22. package/dist/reactive-height-manager/ReactiveHeightManager.svelte.js +0 -200
  23. package/dist/reactive-height-manager/benchmark.d.ts +0 -5
  24. package/dist/utils/resizeObserver.d.ts +0 -89
  25. package/dist/utils/resizeObserver.js +0 -119
  26. /package/dist/{reactive-height-manager → reactive-list-manager}/types.js +0 -0
@@ -89,7 +89,7 @@
89
89
  * - Progressive size adjustment system
90
90
  */
91
91
  import { type SvelteVirtualListProps, type SvelteVirtualListScrollOptions } from './types.js';
92
- declare function $$render<TItem = any>(): {
92
+ declare function $$render<TItem = unknown>(): {
93
93
  props: SvelteVirtualListProps<TItem>;
94
94
  exports: {
95
95
  /**
@@ -153,7 +153,7 @@ declare function $$render<TItem = any>(): {
153
153
  slots: {};
154
154
  events: {};
155
155
  };
156
- declare class __sveltets_Render<TItem = any> {
156
+ declare class __sveltets_Render<TItem = unknown> {
157
157
  props(): ReturnType<typeof $$render<TItem>>['props'];
158
158
  events(): ReturnType<typeof $$render<TItem>>['events'];
159
159
  slots(): ReturnType<typeof $$render<TItem>>['slots'];
@@ -218,10 +218,10 @@ declare class __sveltets_Render<TItem = any> {
218
218
  };
219
219
  }
220
220
  interface $$IsomorphicComponent {
221
- new <TItem = any>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TItem>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TItem>['props']>, ReturnType<__sveltets_Render<TItem>['events']>, ReturnType<__sveltets_Render<TItem>['slots']>> & {
221
+ new <TItem = unknown>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TItem>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TItem>['props']>, ReturnType<__sveltets_Render<TItem>['events']>, ReturnType<__sveltets_Render<TItem>['slots']>> & {
222
222
  $$bindings?: ReturnType<__sveltets_Render<TItem>['bindings']>;
223
223
  } & ReturnType<__sveltets_Render<TItem>['exports']>;
224
- <TItem = any>(internal: unknown, props: ReturnType<__sveltets_Render<TItem>['props']> & {}): ReturnType<__sveltets_Render<TItem>['exports']>;
224
+ <TItem = unknown>(internal: unknown, props: ReturnType<__sveltets_Render<TItem>['props']> & {}): ReturnType<__sveltets_Render<TItem>['exports']>;
225
225
  z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
226
226
  }
227
227
  /**
@@ -287,5 +287,5 @@ interface $$IsomorphicComponent {
287
287
  * MIT License © Humanspeak, Inc.
288
288
  */
289
289
  declare const SvelteVirtualList: $$IsomorphicComponent;
290
- type SvelteVirtualList<TItem = any> = InstanceType<typeof SvelteVirtualList<TItem>>;
290
+ type SvelteVirtualList<TItem = unknown> = InstanceType<typeof SvelteVirtualList<TItem>>;
291
291
  export default SvelteVirtualList;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import SvelteVirtualList from './SvelteVirtualList.svelte';
2
2
  import type { SvelteVirtualListDebugInfo, SvelteVirtualListMode, SvelteVirtualListProps, SvelteVirtualListScrollAlign, SvelteVirtualListScrollOptions } from './types.js';
3
- export default SvelteVirtualList;
4
3
  export type { SvelteVirtualListDebugInfo, SvelteVirtualListMode, SvelteVirtualListProps, SvelteVirtualListScrollAlign, SvelteVirtualListScrollOptions };
4
+ export { ReactiveListManager } from './reactive-list-manager/index.js';
5
+ export type { ListManagerConfig } from './reactive-list-manager/index.js';
6
+ export default SvelteVirtualList;
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
1
  import SvelteVirtualList from './SvelteVirtualList.svelte';
2
+ // Re-export renamed manager from existing package location to avoid churn
3
+ export { ReactiveListManager } from './reactive-list-manager/index.js';
2
4
  export default SvelteVirtualList;
@@ -1,4 +1,4 @@
1
- # ReactiveHeightManager Integration Examples
1
+ # ReactiveListManager Integration Examples
2
2
 
3
3
  > Detailed integration patterns for common use cases
4
4
 
@@ -6,19 +6,19 @@
6
6
 
7
7
  ## SvelteVirtualList Integration
8
8
 
9
- ### 1. Import the ReactiveHeightManager
9
+ ### 1. Import the ReactiveListManager
10
10
 
11
11
  ```typescript
12
- import { ReactiveHeightManager } from '$lib/reactive-height-manager'
12
+ import { ReactiveListManager } from '$lib/reactive-list-manager'
13
13
  ```
14
14
 
15
15
  ### 2. Create the manager instance
16
16
 
17
17
  ```typescript
18
- // Create reactive height manager
19
- const heightManager = new ReactiveHeightManager({
18
+ // Create reactive list manager
19
+ const heightManager = new ReactiveListManager({
20
20
  itemLength: items.length,
21
- estimatedHeight: defaultEstimatedItemHeight
21
+ itemHeight: defaultEstimatedItemHeight
22
22
  })
23
23
 
24
24
  // Update when items change
@@ -91,7 +91,7 @@ let totalHeight = $derived(() => heightManager.totalHeight)
91
91
 
92
92
  ## Type Compatibility
93
93
 
94
- The `ReactiveHeightManager` uses its own types but is designed to be compatible:
94
+ The `ReactiveListManager` uses its own types but is designed to be compatible:
95
95
 
96
96
  ```typescript
97
97
  // If SvelteVirtualList heightChanges are compatible, use directly:
@@ -121,14 +121,14 @@ if (heightManager.hasSufficientMeasurements(20)) {
121
121
 
122
122
  ## Testing
123
123
 
124
- The ReactiveHeightManager comes with comprehensive performance tests:
124
+ The ReactiveListManager comes with comprehensive performance tests:
125
125
 
126
126
  ```bash
127
127
  # Run specific tests
128
- npm run test -- ReactiveHeightManager.test.ts --reporter=verbose
128
+ npm run test -- ReactiveListManager.test.ts --reporter=verbose
129
129
 
130
130
  # Performance benchmarking
131
- import { benchmarkHeightManager } from '$lib/reactive-height-manager'
131
+ import { benchmarkHeightManager } from '$lib/reactive-list-manager'
132
132
 
133
133
  const results = benchmarkHeightManager(10000, 1000, 100)
134
134
  console.log(`Average time: ${results.avgTime.toFixed(2)}ms`)
@@ -1,8 +1,8 @@
1
- # ReactiveHeightManager
1
+ # ReactiveListManager (formerly ReactiveListManager)
2
2
 
3
3
  > A standalone, high-performance reactive height calculation system for virtualized lists
4
4
 
5
- <!-- [![Tests](https://img.shields.io/badge/tests-13%20passing-brightgreen)](./ReactiveHeightManager.test.ts)
5
+ <!-- [![Tests](https://img.shields.io/badge/tests-13%20passing-brightgreen)](./ReactiveListManager.test.ts)
6
6
  [![Performance](https://img.shields.io/badge/performance-1000%20updates%20%3C1ms-blue)](#performance)
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/) -->
8
8
 
@@ -27,7 +27,7 @@ for (let i = 0; i < items.length; i++) {
27
27
  }
28
28
  ```
29
29
 
30
- ReactiveHeightManager processes only **dirty/changed items**:
30
+ ReactiveListManager processes only **dirty/changed items**:
31
31
 
32
32
  ```typescript
33
33
  // ✅ O(dirty items) - Fast and reactive
@@ -39,19 +39,19 @@ const totalHeight = manager.getDerivedTotalHeight()
39
39
 
40
40
  ```bash
41
41
  # If using within svelte-virtual-list project
42
- import { ReactiveHeightManager } from '$lib/reactive-height-manager'
42
+ import { ReactiveListManager } from '$lib/reactive-list-manager'
43
43
 
44
44
  # For standalone usage (copy the module)
45
- cp -r src/lib/reactive-height-manager your-project/src/lib/
45
+ cp -r src/lib/reactive-list-manager your-project/src/lib/
46
46
  ```
47
47
 
48
48
  ## 🚀 Quick Start
49
49
 
50
50
  ```typescript
51
- import { ReactiveHeightManager } from './reactive-height-manager'
51
+ import { ReactiveListManager } from './reactive-list-manager'
52
52
 
53
53
  // Create manager
54
- const manager = new ReactiveHeightManager({
54
+ const manager = new ReactiveListManager({
55
55
  itemLength: 10000,
56
56
  itemHeight: 40
57
57
  })
@@ -76,7 +76,7 @@ console.log(`Total height: ${totalHeight}px`)
76
76
  ### Constructor
77
77
 
78
78
  ```typescript
79
- new ReactiveHeightManager(config: HeightManagerConfig)
79
+ new ReactiveListManager(config: ListManagerConfig)
80
80
  ```
81
81
 
82
82
  **Parameters:**
@@ -128,7 +128,7 @@ Reset all state to initial values.
128
128
 
129
129
  ### Utilities
130
130
 
131
- #### `getDebugInfo(): HeightManagerDebugInfo`
131
+ #### `getDebugInfo(): ListManagerDebugInfo`
132
132
 
133
133
  Get comprehensive debug information.
134
134
 
@@ -152,7 +152,7 @@ Check if manager has sufficient measurement data.
152
152
 
153
153
  ```typescript
154
154
  // Create manager
155
- const heightManager = new ReactiveHeightManager({
155
+ const heightManager = new ReactiveListManager({
156
156
  itemLength: items.length,
157
157
  estimatedHeight: defaultEstimatedItemHeight
158
158
  })
@@ -188,9 +188,9 @@ let totalHeight = $derived(() => heightManager.totalHeight)
188
188
  ### Standalone Usage
189
189
 
190
190
  ```typescript
191
- import { ReactiveHeightManager, benchmarkHeightManager } from './reactive-height-manager'
191
+ import { ReactiveListManager, benchmarkHeightManager } from './reactive-list-manager'
192
192
 
193
- const manager = new ReactiveHeightManager({ itemLength: 1000, estimatedHeight: 50 })
193
+ const manager = new ReactiveListManager({ itemLength: 1000, estimatedHeight: 50 })
194
194
 
195
195
  // Performance monitoring
196
196
  const results = benchmarkHeightManager(10000, 1000, 100)
@@ -234,10 +234,10 @@ manager.totalHeight // ~0.01ms
234
234
 
235
235
  ```bash
236
236
  # Run all tests
237
- npm run test -- ReactiveHeightManager.test.ts
237
+ npm run test -- ReactiveListManager.test.ts
238
238
 
239
239
  # Verbose output
240
- npm run test -- ReactiveHeightManager.test.ts --reporter=verbose
240
+ npm run test -- ReactiveListManager.test.ts --reporter=verbose
241
241
 
242
242
  # Performance benchmarking
243
243
  npm run test -- --grep "Performance Tests"
@@ -286,12 +286,12 @@ interface HeightChange {
286
286
  readonly newHeight: number
287
287
  }
288
288
 
289
- interface HeightManagerConfig {
289
+ interface ListManagerConfig {
290
290
  itemLength: number
291
291
  estimatedHeight: number
292
292
  }
293
293
 
294
- interface HeightManagerDebugInfo {
294
+ interface ListManagerDebugInfo {
295
295
  totalMeasuredHeight: number
296
296
  measuredCount: number
297
297
  itemLength: number
@@ -310,7 +310,7 @@ interface HeightManagerDebugInfo {
310
310
 
311
311
  ## 🤝 Contributing
312
312
 
313
- 1. Run tests: `npm run test -- ReactiveHeightManager.test.ts`
313
+ 1. Run tests: `npm run test -- ReactiveListManager.test.ts`
314
314
  2. Add performance benchmarks for new features
315
315
  3. Maintain O(1) or O(dirty) complexity
316
316
  4. Update type definitions
@@ -0,0 +1,221 @@
1
+ import type { HeightChange, ListManagerConfig, ListManagerDebugInfo } from './types.js';
2
+ /**
3
+ * ReactiveListManager - 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 ReactiveListManager({ 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 ReactiveListManager {
26
+ #private;
27
+ private _totalMeasuredHeight;
28
+ private _measuredCount;
29
+ private _itemLength;
30
+ private _itemHeight;
31
+ private _averageHeight;
32
+ private _totalHeight;
33
+ private _measuredFlags;
34
+ private _initialized;
35
+ private _scrollTop;
36
+ private _containerElement;
37
+ private _viewportElement;
38
+ private _internalDebug;
39
+ private _isReady;
40
+ private _dynamicUpdateInProgress;
41
+ private _dynamicUpdateDepth;
42
+ private _itemsWrapperElement;
43
+ private _gridDetected;
44
+ private _gridColumns;
45
+ private _gridObserver;
46
+ private _mutationObserver;
47
+ private _heightCache;
48
+ private _scheduler;
49
+ private recomputeDerivedHeights;
50
+ private recomputeIsReady;
51
+ private scheduleRecomputeDerivedHeights;
52
+ /**
53
+ * Get total measured height of all measured items
54
+ */
55
+ get totalMeasuredHeight(): number;
56
+ /**
57
+ * Get count of items that have been measured
58
+ */
59
+ get measuredCount(): number;
60
+ /**
61
+ * Get total number of items in the list
62
+ */
63
+ get itemLength(): number;
64
+ /**
65
+ * Get/Set the height to use for unmeasured items (reactive)
66
+ */
67
+ get itemHeight(): number;
68
+ set itemHeight(value: number);
69
+ /**
70
+ * Get/Set initialized flag
71
+ */
72
+ get initialized(): boolean;
73
+ set initialized(value: boolean);
74
+ /**
75
+ * Get/Set current scrollTop (reactive)
76
+ */
77
+ get scrollTop(): number;
78
+ set scrollTop(value: number);
79
+ /**
80
+ * Container element reference (reactive, nullable)
81
+ *
82
+ * Why both `containerElement` and `container` exist:
83
+ * - `containerElement` is a nullable, reactive reference intended for Svelte `bind:this` wiring
84
+ * from components. It may be temporarily null during mount/unmount and is safe to read as
85
+ * a possibly-null value. Setting it more than once is prohibited to catch wiring bugs early.
86
+ * - `container` is the non-null accessor for internal consumers that require a definite
87
+ * HTMLElement once the manager is wired. It throws until the manager is `isReady === true`
88
+ * (i.e., both container and viewport are present). Use this when you want a guaranteed DOM node.
89
+ */
90
+ get containerElement(): HTMLElement | null;
91
+ get container(): HTMLElement;
92
+ set containerElement(el: HTMLElement | null);
93
+ /**
94
+ * Viewport element reference (reactive, nullable)
95
+ *
96
+ * Why both `viewportElement` and `viewport` exist:
97
+ * - `viewportElement` is a nullable, reactive reference intended for Svelte `bind:this` wiring
98
+ * from components. It may be temporarily null during mount/unmount and is safe to read as
99
+ * a possibly-null value. Setting it more than once is prohibited to catch wiring bugs early.
100
+ * - `viewport` is the non-null accessor for internal consumers that require a definite
101
+ * HTMLElement once the manager is wired. It throws until the manager is `isReady === true`
102
+ * (i.e., both container and viewport are present). Use this when you want a guaranteed DOM node.
103
+ */
104
+ get viewportElement(): HTMLElement | null;
105
+ get viewport(): HTMLElement;
106
+ set viewportElement(el: HTMLElement | null);
107
+ /**
108
+ * Items wrapper element reference (reactive, nullable)
109
+ *
110
+ * Used for CSS-based grid detection. When set, the manager will auto-detect
111
+ * whether the items container is a grid and how many columns it defines.
112
+ */
113
+ get itemsWrapperElement(): HTMLElement | null;
114
+ set itemsWrapperElement(el: HTMLElement | null);
115
+ /** Whether a CSS grid was detected on the items wrapper */
116
+ get gridDetected(): boolean;
117
+ /** Number of columns when a grid is detected; 1 when not a grid */
118
+ get gridColumns(): number;
119
+ get isReady(): boolean;
120
+ /**
121
+ * Whether a dynamic update is currently running.
122
+ * Set to true while `runDynamicUpdate` is executing.
123
+ */
124
+ get isDynamicUpdateInProgress(): boolean;
125
+ /**
126
+ * Begin a dynamic update. Handles nested calls: the first call disables UA scroll anchoring,
127
+ * subsequent calls just increment depth. Safe to call when not wired; styles are only toggled
128
+ * when both container and viewport are ready.
129
+ */
130
+ startDynamicUpdate(): void;
131
+ /**
132
+ * End a dynamic update started by `startDynamicUpdate`. Handles nesting: only the final
133
+ * corresponding end call re-enables UA scroll anchoring. Guards against underflow.
134
+ */
135
+ endDynamicUpdate(): void;
136
+ /**
137
+ * Run a dynamic update with UA scroll anchoring disabled, then restore it.
138
+ * Accepts a sync or async function and ensures `overflow-anchor` is toggled
139
+ * around the operation. If the manager isn't ready yet, it simply executes `fn`.
140
+ */
141
+ runDynamicUpdate<T>(fn: () => T | Promise<T>): Promise<T>;
142
+ /**
143
+ * Get the calculated average height of measured items
144
+ * Falls back to itemHeight if no items have been measured yet
145
+ */
146
+ get averageHeight(): number;
147
+ /**
148
+ * Get the reactive total height of all items (measured + estimated)
149
+ * This automatically updates when any dependencies change
150
+ */
151
+ get totalHeight(): number;
152
+ /**
153
+ * Test helper: force a recompute immediately (bypasses scheduler).
154
+ */
155
+ flushRecompute: () => void;
156
+ /**
157
+ * Read-only view of measured heights cache
158
+ */
159
+ getHeightCache(): Readonly<Record<number, number>>;
160
+ /**
161
+ * Create a new ReactiveListManager instance
162
+ *
163
+ * @param config - Configuration object containing itemLength and itemHeight
164
+ */
165
+ constructor(config: ListManagerConfig);
166
+ /**
167
+ * Process height changes incrementally - O(dirty items) instead of O(all items)
168
+ *
169
+ * This is the core optimization: instead of recalculating totals for all items,
170
+ * we only process the items that have changed, maintaining running totals.
171
+ *
172
+ * Accepts any object that has index, oldHeight, and newHeight properties,
173
+ * allowing consumers to pass objects with additional fields.
174
+ *
175
+ * @param dirtyResults - Array of height changes to process
176
+ */
177
+ processDirtyHeights(dirtyResults: HeightChange[]): void;
178
+ /**
179
+ * Update when items array length changes
180
+ *
181
+ * @param newLength - New total number of items
182
+ */
183
+ updateItemLength(newLength: number): void;
184
+ /**
185
+ * Update estimated height for unmeasured items
186
+ *
187
+ * @param newEstimatedHeight - New estimated height
188
+ */
189
+ updateEstimatedHeight(newEstimatedHeight: number): void;
190
+ /**
191
+ * Set a single measured height and update totals
192
+ */
193
+ setMeasuredHeight(index: number, height: number): void;
194
+ /**
195
+ * Reset all state to initial values
196
+ *
197
+ * Useful for testing or when completely reinitializing the list
198
+ */
199
+ reset(): void;
200
+ /**
201
+ * Get comprehensive debug information
202
+ *
203
+ * @returns Debug information object
204
+ */
205
+ getDebugInfo(): ListManagerDebugInfo;
206
+ /**
207
+ * Get the percentage of items that have been measured
208
+ *
209
+ * @returns Percentage (0-100) of measured items
210
+ */
211
+ getMeasurementCoverage(): number;
212
+ /**
213
+ * Check if the manager has sufficient measurement data
214
+ *
215
+ * @param threshold - Minimum percentage of items that should be measured (default: 10)
216
+ * @returns true if coverage meets threshold
217
+ */
218
+ hasSufficientMeasurements(threshold?: number): boolean;
219
+ /** Public: Re-run CSS grid detection immediately */
220
+ recomputeGridDetection(): void;
221
+ }