mtrl-addons 0.2.2 → 0.2.4

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 (129) hide show
  1. package/{src/components/index.ts → dist/components/index.d.ts} +0 -2
  2. package/dist/components/vlist/config.d.ts +86 -0
  3. package/{src/components/vlist/constants.ts → dist/components/vlist/constants.d.ts} +10 -11
  4. package/dist/components/vlist/features/api.d.ts +7 -0
  5. package/{src/components/vlist/features/index.ts → dist/components/vlist/features/index.d.ts} +0 -2
  6. package/dist/components/vlist/features/selection.d.ts +6 -0
  7. package/dist/components/vlist/features/viewport.d.ts +9 -0
  8. package/dist/components/vlist/features.d.ts +31 -0
  9. package/{src/components/vlist/index.ts → dist/components/vlist/index.d.ts} +1 -10
  10. package/dist/components/vlist/types.d.ts +596 -0
  11. package/dist/components/vlist/vlist.d.ts +29 -0
  12. package/dist/core/compose/features/gestures/index.d.ts +86 -0
  13. package/dist/core/compose/features/gestures/longpress.d.ts +85 -0
  14. package/dist/core/compose/features/gestures/pan.d.ts +108 -0
  15. package/dist/core/compose/features/gestures/pinch.d.ts +111 -0
  16. package/dist/core/compose/features/gestures/rotate.d.ts +111 -0
  17. package/dist/core/compose/features/gestures/swipe.d.ts +149 -0
  18. package/dist/core/compose/features/gestures/tap.d.ts +79 -0
  19. package/{src/core/compose/features/index.ts → dist/core/compose/features/index.d.ts} +1 -2
  20. package/{src/core/compose/index.ts → dist/core/compose/index.d.ts} +2 -11
  21. package/{src/core/gestures/index.ts → dist/core/gestures/index.d.ts} +1 -20
  22. package/dist/core/gestures/longpress.d.ts +23 -0
  23. package/dist/core/gestures/manager.d.ts +14 -0
  24. package/dist/core/gestures/pan.d.ts +12 -0
  25. package/dist/core/gestures/pinch.d.ts +14 -0
  26. package/dist/core/gestures/rotate.d.ts +14 -0
  27. package/dist/core/gestures/swipe.d.ts +20 -0
  28. package/dist/core/gestures/tap.d.ts +12 -0
  29. package/dist/core/gestures/types.d.ts +320 -0
  30. package/dist/core/gestures/utils.d.ts +57 -0
  31. package/dist/core/index.d.ts +13 -0
  32. package/dist/core/layout/config.d.ts +33 -0
  33. package/dist/core/layout/index.d.ts +51 -0
  34. package/dist/core/layout/jsx.d.ts +65 -0
  35. package/dist/core/layout/schema.d.ts +112 -0
  36. package/dist/core/layout/types.d.ts +69 -0
  37. package/dist/core/viewport/constants.d.ts +105 -0
  38. package/dist/core/viewport/features/base.d.ts +14 -0
  39. package/dist/core/viewport/features/collection.d.ts +41 -0
  40. package/dist/core/viewport/features/events.d.ts +13 -0
  41. package/{src/core/viewport/features/index.ts → dist/core/viewport/features/index.d.ts} +0 -7
  42. package/dist/core/viewport/features/item-size.d.ts +30 -0
  43. package/dist/core/viewport/features/loading.d.ts +34 -0
  44. package/dist/core/viewport/features/momentum.d.ts +17 -0
  45. package/dist/core/viewport/features/performance.d.ts +53 -0
  46. package/dist/core/viewport/features/placeholders.d.ts +38 -0
  47. package/dist/core/viewport/features/rendering.d.ts +16 -0
  48. package/dist/core/viewport/features/scrollbar.d.ts +26 -0
  49. package/dist/core/viewport/features/scrolling.d.ts +16 -0
  50. package/dist/core/viewport/features/utils.d.ts +43 -0
  51. package/dist/core/viewport/features/virtual.d.ts +18 -0
  52. package/{src/core/viewport/index.ts → dist/core/viewport/index.d.ts} +1 -17
  53. package/dist/core/viewport/types.d.ts +96 -0
  54. package/dist/core/viewport/utils/speed-tracker.d.ts +22 -0
  55. package/dist/core/viewport/viewport.d.ts +11 -0
  56. package/{src/index.ts → dist/index.d.ts} +0 -4
  57. package/dist/index.js +5143 -0
  58. package/dist/index.mjs +5111 -0
  59. package/dist/styles.css +254 -0
  60. package/dist/styles.css.map +1 -0
  61. package/package.json +16 -2
  62. package/.cursorrules +0 -117
  63. package/AI.md +0 -39
  64. package/CLAUDE.md +0 -882
  65. package/build.js +0 -377
  66. package/index.ts +0 -7
  67. package/scripts/analyze-orphaned-functions.ts +0 -387
  68. package/scripts/debug/vlist-selection.ts +0 -121
  69. package/src/components/vlist/config.ts +0 -323
  70. package/src/components/vlist/features/api.ts +0 -626
  71. package/src/components/vlist/features/selection.ts +0 -436
  72. package/src/components/vlist/features/viewport.ts +0 -59
  73. package/src/components/vlist/features.ts +0 -112
  74. package/src/components/vlist/types.ts +0 -723
  75. package/src/components/vlist/vlist.ts +0 -92
  76. package/src/core/compose/features/gestures/index.ts +0 -227
  77. package/src/core/compose/features/gestures/longpress.ts +0 -383
  78. package/src/core/compose/features/gestures/pan.ts +0 -424
  79. package/src/core/compose/features/gestures/pinch.ts +0 -475
  80. package/src/core/compose/features/gestures/rotate.ts +0 -485
  81. package/src/core/compose/features/gestures/swipe.ts +0 -492
  82. package/src/core/compose/features/gestures/tap.ts +0 -334
  83. package/src/core/gestures/longpress.ts +0 -68
  84. package/src/core/gestures/manager.ts +0 -418
  85. package/src/core/gestures/pan.ts +0 -48
  86. package/src/core/gestures/pinch.ts +0 -58
  87. package/src/core/gestures/rotate.ts +0 -58
  88. package/src/core/gestures/swipe.ts +0 -66
  89. package/src/core/gestures/tap.ts +0 -45
  90. package/src/core/gestures/types.ts +0 -387
  91. package/src/core/gestures/utils.ts +0 -128
  92. package/src/core/index.ts +0 -43
  93. package/src/core/layout/config.ts +0 -102
  94. package/src/core/layout/index.ts +0 -168
  95. package/src/core/layout/jsx.ts +0 -174
  96. package/src/core/layout/schema.ts +0 -1044
  97. package/src/core/layout/types.ts +0 -95
  98. package/src/core/viewport/constants.ts +0 -145
  99. package/src/core/viewport/features/base.ts +0 -73
  100. package/src/core/viewport/features/collection.ts +0 -1182
  101. package/src/core/viewport/features/events.ts +0 -130
  102. package/src/core/viewport/features/item-size.ts +0 -271
  103. package/src/core/viewport/features/loading.ts +0 -263
  104. package/src/core/viewport/features/momentum.ts +0 -269
  105. package/src/core/viewport/features/performance.ts +0 -161
  106. package/src/core/viewport/features/placeholders.ts +0 -335
  107. package/src/core/viewport/features/rendering.ts +0 -962
  108. package/src/core/viewport/features/scrollbar.ts +0 -434
  109. package/src/core/viewport/features/scrolling.ts +0 -634
  110. package/src/core/viewport/features/utils.ts +0 -94
  111. package/src/core/viewport/features/virtual.ts +0 -525
  112. package/src/core/viewport/types.ts +0 -133
  113. package/src/core/viewport/utils/speed-tracker.ts +0 -79
  114. package/src/core/viewport/viewport.ts +0 -265
  115. package/test/benchmarks/layout/advanced.test.ts +0 -656
  116. package/test/benchmarks/layout/comparison.test.ts +0 -519
  117. package/test/benchmarks/layout/performance-comparison.test.ts +0 -274
  118. package/test/benchmarks/layout/real-components.test.ts +0 -733
  119. package/test/benchmarks/layout/simple.test.ts +0 -321
  120. package/test/benchmarks/layout/stress.test.ts +0 -990
  121. package/test/collection/basic.test.ts +0 -304
  122. package/test/components/vlist-selection.test.ts +0 -240
  123. package/test/components/vlist.test.ts +0 -63
  124. package/test/core/collection/adapter.test.ts +0 -161
  125. package/test/core/collection/collection.test.ts +0 -394
  126. package/test/core/layout/layout.test.ts +0 -201
  127. package/test/utils/dom-helpers.ts +0 -275
  128. package/test/utils/performance-helpers.ts +0 -392
  129. package/tsconfig.json +0 -20
@@ -1,130 +0,0 @@
1
- // src/core/viewport/features/events.ts
2
-
3
- /**
4
- * Events Feature - Centralized event system for viewport
5
- * Provides event emission and subscription for inter-feature communication
6
- */
7
-
8
- import type { ViewportContext, ViewportComponent } from "../types";
9
-
10
- export interface EventsConfig {
11
- debug?: boolean;
12
- }
13
-
14
- /**
15
- * Events feature for viewport
16
- * Centralizes all event handling for viewport features
17
- */
18
- export const withEvents = (config: EventsConfig = {}) => {
19
- return <T extends ViewportContext & ViewportComponent>(component: T): T => {
20
- const { debug = false } = config;
21
-
22
- // Event listeners map
23
- const listeners = new Map<string, Set<Function>>();
24
-
25
- // Emit an event
26
- const emit = (event: string, data?: any) => {
27
- // if (debug) {
28
- // console.log(`[Events] Emit: ${event}`, data);
29
- // }
30
-
31
- const eventListeners = listeners.get(event);
32
- if (eventListeners) {
33
- eventListeners.forEach((listener) => {
34
- try {
35
- listener(data);
36
- } catch (error) {
37
- console.error(`[Events] Error in listener for ${event}:`, error);
38
- }
39
- });
40
- }
41
- };
42
-
43
- // Subscribe to an event
44
- const on = (event: string, handler: Function): (() => void) => {
45
- if (!listeners.has(event)) {
46
- listeners.set(event, new Set());
47
- }
48
-
49
- listeners.get(event)!.add(handler);
50
-
51
- // if (debug) {
52
- // console.log(`[Events] Subscribed to: ${event}`);
53
- // }
54
-
55
- // Return unsubscribe function
56
- return () => {
57
- const eventListeners = listeners.get(event);
58
- if (eventListeners) {
59
- eventListeners.delete(handler);
60
- if (eventListeners.size === 0) {
61
- listeners.delete(event);
62
- }
63
- }
64
- };
65
- };
66
-
67
- // Subscribe to an event once
68
- const once = (event: string, handler: Function): (() => void) => {
69
- const wrappedHandler = (data: any) => {
70
- handler(data);
71
- off(event, wrappedHandler);
72
- };
73
- return on(event, wrappedHandler);
74
- };
75
-
76
- // Unsubscribe from an event
77
- const off = (event: string, handler: Function) => {
78
- const eventListeners = listeners.get(event);
79
- if (eventListeners) {
80
- eventListeners.delete(handler);
81
- if (eventListeners.size === 0) {
82
- listeners.delete(event);
83
- }
84
- }
85
- };
86
-
87
- // Clear all listeners for an event
88
- const clear = (event?: string) => {
89
- if (event) {
90
- listeners.delete(event);
91
- } else {
92
- listeners.clear();
93
- }
94
- };
95
-
96
- // Add event methods to component
97
- component.emit = emit;
98
- component.on = on;
99
- component.once = once;
100
- component.off = off;
101
-
102
- // Add events API to viewport
103
- (component.viewport as any).events = {
104
- emit,
105
- on,
106
- once,
107
- off,
108
- clear,
109
- getListenerCount: (event?: string) => {
110
- if (event) {
111
- return listeners.get(event)?.size || 0;
112
- }
113
- let total = 0;
114
- listeners.forEach((set) => (total += set.size));
115
- return total;
116
- },
117
- };
118
-
119
- // Clean up on destroy
120
- if ("destroy" in component && typeof component.destroy === "function") {
121
- const originalDestroy = component.destroy;
122
- component.destroy = () => {
123
- clear();
124
- originalDestroy?.();
125
- };
126
- }
127
-
128
- return component;
129
- };
130
- };
@@ -1,271 +0,0 @@
1
- /**
2
- * Item Size Management
3
- * Handles item measurement, caching, and size estimation for virtual scrolling
4
- * Works with both vertical (height) and horizontal (width) orientations
5
- */
6
-
7
- export interface ItemSizeManager {
8
- // Measurement
9
- measureItem(
10
- element: HTMLElement,
11
- index: number,
12
- orientation?: "vertical" | "horizontal",
13
- ): number;
14
-
15
- // Cache management
16
- hasMeasuredSize(index: number): boolean;
17
- getMeasuredSize(index: number): number;
18
- getMeasuredSizes(): Map<number, number>;
19
- cacheItemSize(index: number, size: number): void;
20
- clearCache(): void;
21
-
22
- // Size estimation
23
- getItemSize(): number;
24
- updateItemSize(): void;
25
-
26
- // Additional utilities
27
- calculateTotalSize(totalItems?: number): number;
28
- getStats(): any;
29
-
30
- // Callbacks
31
- onSizeUpdated?: (totalSize: number) => void;
32
- onItemSizeChanged?: (newEstimate: number) => void;
33
- }
34
-
35
- export interface ItemSizeConfig {
36
- initialEstimate?: number;
37
- orientation?: "vertical" | "horizontal";
38
- cacheSize?: number;
39
- onSizeUpdated?: (totalSize: number) => void;
40
- onItemSizeChanged?: (newEstimate: number) => void;
41
- }
42
-
43
- /**
44
- * Creates an item size manager for measuring and caching item dimensions
45
- */
46
- export const createItemSizeManager = (
47
- config: ItemSizeConfig = {},
48
- ): ItemSizeManager => {
49
- const {
50
- initialEstimate = 60,
51
- orientation = "vertical",
52
- cacheSize = 1000,
53
- onSizeUpdated,
54
- onItemSizeChanged,
55
- } = config;
56
-
57
- // Size cache - stores actual measured sizes
58
- const measuredSizes = new Map<number, number>();
59
- let currentItemSize = initialEstimate;
60
-
61
- // Batching state for performance optimization
62
- let batchUpdateTimeout: number | null = null;
63
- let pendingMeasurements = 0;
64
- let batchStartTime = 0;
65
-
66
- /**
67
- * Cache a specific item size with cache size management
68
- */
69
- const cacheItemSize = (index: number, size: number): void => {
70
- if (measuredSizes.size >= cacheSize) {
71
- // Remove oldest entries (10% of cache size)
72
- const entries = Array.from(measuredSizes.entries());
73
- entries.slice(0, Math.floor(cacheSize * 0.1)).forEach(([key]) => {
74
- measuredSizes.delete(key);
75
- });
76
- }
77
- measuredSizes.set(index, size);
78
- };
79
-
80
- /**
81
- * Trigger batched updates after measurements are complete
82
- */
83
- const triggerBatchedUpdates = (): void => {
84
- // Update estimated size based on all measurements
85
- updateItemSize();
86
-
87
- // Notify about total size update
88
- if (onSizeUpdated) {
89
- const totalSize = calculateTotalSize();
90
- onSizeUpdated(totalSize);
91
- }
92
-
93
- // Reset batch state
94
- pendingMeasurements = 0;
95
- batchUpdateTimeout = null;
96
- };
97
-
98
- /**
99
- * Schedule batched updates (debounced)
100
- */
101
- const scheduleBatchedUpdates = (): void => {
102
- if (batchUpdateTimeout) {
103
- clearTimeout(batchUpdateTimeout);
104
- }
105
-
106
- // Short timeout to batch rapid measurements
107
- batchUpdateTimeout = window.setTimeout(() => {
108
- const batchDuration = Date.now() - batchStartTime;
109
- triggerBatchedUpdates();
110
- }, 16); // ~1 frame delay to batch measurements
111
- };
112
-
113
- /**
114
- * Measure actual item size and update cache (with batching)
115
- */
116
- const measureItem = (
117
- element: HTMLElement,
118
- index: number,
119
- measureOrientation?: "vertical" | "horizontal",
120
- ): number => {
121
- if (!element || index < 0) {
122
- return currentItemSize;
123
- }
124
-
125
- const actualOrientation = measureOrientation || orientation;
126
- const size =
127
- actualOrientation === "vertical"
128
- ? element.offsetHeight
129
- : element.offsetWidth;
130
-
131
- if (size > 0) {
132
- const previousSize = measuredSizes.get(index);
133
- cacheItemSize(index, size);
134
-
135
- // Track batch state
136
- if (pendingMeasurements === 0) {
137
- batchStartTime = Date.now();
138
- }
139
- pendingMeasurements++;
140
-
141
- // Schedule batched updates instead of immediate callbacks
142
- scheduleBatchedUpdates();
143
-
144
- return size;
145
- }
146
-
147
- return currentItemSize;
148
- };
149
-
150
- /**
151
- * Update estimated item size based on measured sizes (with change threshold)
152
- */
153
- const updateItemSize = (): void => {
154
- if (measuredSizes.size === 0) return;
155
-
156
- const sizes = Array.from(measuredSizes.values());
157
- const average = sizes.reduce((sum, size) => sum + size, 0) / sizes.length;
158
- const newEstimate = Math.max(1, Math.round(average));
159
-
160
- // Only update if the change is significant (>2px or >5% change)
161
- const changeThreshold = Math.max(2, Math.round(currentItemSize * 0.05));
162
- const absoluteChange = Math.abs(newEstimate - currentItemSize);
163
-
164
- if (absoluteChange >= changeThreshold) {
165
- const previousEstimate = currentItemSize;
166
- currentItemSize = newEstimate;
167
-
168
- if (onItemSizeChanged) {
169
- onItemSizeChanged(newEstimate);
170
- }
171
- } else if (absoluteChange > 0) {
172
- // Silent update for small changes
173
- currentItemSize = newEstimate;
174
- }
175
- };
176
-
177
- /**
178
- * Calculate total size for a given number of items
179
- */
180
- const calculateTotalSize = (totalItems?: number): number => {
181
- if (!totalItems) {
182
- // Calculate based on measured items only
183
- return Array.from(measuredSizes.values()).reduce(
184
- (sum, size) => sum + size,
185
- 0,
186
- );
187
- }
188
-
189
- let totalSize = 0;
190
- for (let i = 0; i < totalItems; i++) {
191
- totalSize += measuredSizes.get(i) || currentItemSize;
192
- }
193
- return totalSize;
194
- };
195
-
196
- /**
197
- * Get size for a specific item (measured or estimated)
198
- */
199
- const getMeasuredSize = (index: number): number => {
200
- return measuredSizes.get(index) || currentItemSize;
201
- };
202
-
203
- /**
204
- * Check if item has been measured
205
- */
206
- const hasMeasuredSize = (index: number): boolean => {
207
- return measuredSizes.has(index);
208
- };
209
-
210
- /**
211
- * Clear all cached measurements
212
- */
213
- const clearCache = (): void => {
214
- const cacheSize = measuredSizes.size;
215
- measuredSizes.clear();
216
- };
217
-
218
- /**
219
- * Get current estimated item size
220
- */
221
- const getItemSize = (): number => {
222
- return currentItemSize;
223
- };
224
-
225
- /**
226
- * Get all measured sizes (for compatibility)
227
- */
228
- const getMeasuredSizes = (): Map<number, number> => {
229
- return new Map(measuredSizes);
230
- };
231
-
232
- /**
233
- * Get cache statistics
234
- */
235
- const getStats = () => {
236
- return {
237
- cachedItems: measuredSizes.size,
238
- estimatedSize: currentItemSize,
239
- cacheSize: cacheSize,
240
- minSize:
241
- measuredSizes.size > 0
242
- ? Math.min(...measuredSizes.values())
243
- : currentItemSize,
244
- maxSize:
245
- measuredSizes.size > 0
246
- ? Math.max(...measuredSizes.values())
247
- : currentItemSize,
248
- orientation: orientation,
249
- };
250
- };
251
-
252
- return {
253
- // Core API
254
- measureItem,
255
- hasMeasuredSize,
256
- getMeasuredSize,
257
- getMeasuredSizes,
258
- clearCache,
259
- getItemSize,
260
- updateItemSize,
261
- cacheItemSize,
262
-
263
- // Additional utilities
264
- calculateTotalSize,
265
- getStats,
266
-
267
- // Callbacks
268
- onSizeUpdated,
269
- onItemSizeChanged,
270
- };
271
- };
@@ -1,263 +0,0 @@
1
- /**
2
- * Loading - Velocity-based intelligent data loading for viewport
3
- *
4
- * This viewport module manages data loading based on scroll velocity.
5
- * When scrolling fast, it cancels loads to prevent server overload.
6
- */
7
-
8
- import type { ItemRange } from "../types";
9
- type ListManagerComponent = any;
10
- import { VIEWPORT_CONSTANTS } from "../constants";
11
-
12
- export interface LoadingConfig {
13
- cancelLoadThreshold?: number; // Velocity (px/ms) above which all loads are cancelled
14
- maxConcurrentRequests?: number;
15
- enableRequestQueue?: boolean;
16
- }
17
-
18
- export interface LoadingManager {
19
- requestLoad(range: ItemRange, priority: "high" | "normal" | "low"): void;
20
- updateVelocity(velocity: number, direction: "forward" | "backward"): void;
21
- cancelPendingLoads(): void;
22
- getStats(): LoadingStats;
23
- isRangeLoading(range: ItemRange): boolean;
24
- }
25
-
26
- interface LoadingStats {
27
- pendingRequests: number;
28
- completedRequests: number;
29
- failedRequests: number;
30
- cancelledRequests: number;
31
- currentVelocity: number;
32
- canLoad: boolean;
33
- queuedRequests: number;
34
- }
35
-
36
- interface QueuedRequest {
37
- range: ItemRange;
38
- priority: "high" | "normal" | "low";
39
- timestamp: number;
40
- }
41
-
42
- /**
43
- * Creates a loading manager that handles data loading based on scroll velocity
44
- */
45
- export const createLoadingManager = (
46
- component: ListManagerComponent,
47
- config: LoadingConfig = {}
48
- ): LoadingManager => {
49
- const {
50
- cancelLoadThreshold = VIEWPORT_CONSTANTS.LOADING.CANCEL_THRESHOLD,
51
- maxConcurrentRequests = VIEWPORT_CONSTANTS.REQUEST_QUEUE
52
- .MAX_ACTIVE_REQUESTS,
53
- enableRequestQueue = VIEWPORT_CONSTANTS.REQUEST_QUEUE.ENABLED,
54
- } = config;
55
-
56
- // State
57
- let currentVelocity = 0;
58
- let scrollDirection: "forward" | "backward" = "forward";
59
- let activeRequests = 0;
60
-
61
- // Request queue
62
- let requestQueue: QueuedRequest[] = [];
63
-
64
- // Track active requests to prevent duplicates
65
- const activeRanges = new Set<string>();
66
-
67
- // Stats
68
- let completedRequests = 0;
69
- let failedRequests = 0;
70
- let cancelledRequests = 0;
71
-
72
- /**
73
- * Get range key for deduplication
74
- */
75
- const getRangeKey = (range: ItemRange): string => {
76
- return `${range.start}-${range.end}`;
77
- };
78
-
79
- /**
80
- * Check if we should load data at current velocity
81
- */
82
- const canLoad = (): boolean => {
83
- return currentVelocity <= cancelLoadThreshold;
84
- };
85
-
86
- /**
87
- * Update current velocity
88
- */
89
- const updateVelocity = (
90
- velocity: number,
91
- direction: "forward" | "backward"
92
- ): void => {
93
- const previousVelocity = currentVelocity;
94
- currentVelocity = Math.abs(velocity);
95
- scrollDirection = direction;
96
-
97
- // When velocity drops below threshold (including reaching zero), process queued requests
98
- if (
99
- previousVelocity > cancelLoadThreshold &&
100
- currentVelocity <= cancelLoadThreshold
101
- ) {
102
- processQueue();
103
- }
104
- };
105
-
106
- /**
107
- * Process the request queue
108
- */
109
- const processQueue = (): void => {
110
- if (!enableRequestQueue) {
111
- return;
112
- }
113
-
114
- let processed = 0;
115
- while (requestQueue.length > 0 && activeRequests < maxConcurrentRequests) {
116
- const request = requestQueue.shift();
117
- if (request) {
118
- executeLoad(request.range, request.priority);
119
- processed++;
120
- }
121
- }
122
- };
123
-
124
- /**
125
- * Request to load a range with priority
126
- */
127
- const requestLoad = (
128
- range: ItemRange,
129
- priority: "high" | "normal" | "low"
130
- ): void => {
131
- const rangeKey = getRangeKey(range);
132
-
133
- // Check if already loading this range
134
- if (activeRanges.has(rangeKey)) {
135
- return;
136
- }
137
-
138
- // Check velocity for all requests
139
- if (!canLoad()) {
140
- // console.log(
141
- // `🚫 [LOADING] Request cancelled - velocity ${currentVelocity.toFixed(
142
- // 2
143
- // )} px/ms exceeds threshold ${cancelLoadThreshold} px/ms`
144
- // );
145
- cancelledRequests++;
146
- return;
147
- }
148
-
149
- // For higher velocities or zero velocity, execute immediately
150
- // If velocity is low, execute immediately
151
- if (activeRequests < maxConcurrentRequests) {
152
- executeLoad(range, priority);
153
- } else if (enableRequestQueue) {
154
- // Add to queue if we're at capacity
155
- requestQueue.push({
156
- range,
157
- priority,
158
- timestamp: Date.now(),
159
- });
160
-
161
- // Enforce max queue size
162
- if (
163
- requestQueue.length > VIEWPORT_CONSTANTS.REQUEST_QUEUE.MAX_QUEUE_SIZE
164
- ) {
165
- const removed = requestQueue.splice(
166
- 0,
167
- requestQueue.length - VIEWPORT_CONSTANTS.REQUEST_QUEUE.MAX_QUEUE_SIZE
168
- );
169
- cancelledRequests += removed.length;
170
- }
171
- } else {
172
- // Queue is disabled and we're at capacity - drop the request
173
-
174
- cancelledRequests++;
175
- }
176
- };
177
-
178
- /**
179
- * Execute load immediately
180
- */
181
- const executeLoad = (
182
- range: ItemRange,
183
- priority: "high" | "normal" | "low"
184
- ): void => {
185
- const rangeKey = getRangeKey(range);
186
-
187
- // Double-check for duplicates
188
- if (activeRanges.has(rangeKey)) {
189
- return;
190
- }
191
-
192
- activeRequests++;
193
- activeRanges.add(rangeKey);
194
-
195
- // Request data from collection
196
- const collection = (component as any).collection;
197
- if (collection && typeof collection.loadMissingRanges === "function") {
198
- collection
199
- .loadMissingRanges(range, "loading:loadRange")
200
- .then(() => {
201
- activeRequests--;
202
- activeRanges.delete(rangeKey);
203
- completedRequests++;
204
- // Process more requests from queue
205
- processQueue();
206
- })
207
- .catch((error: any) => {
208
- activeRequests--;
209
- activeRanges.delete(rangeKey);
210
- failedRequests++;
211
- // Process more requests from queue even on failure
212
- processQueue();
213
- });
214
- } else {
215
- activeRequests--;
216
- activeRanges.delete(rangeKey);
217
- }
218
- };
219
-
220
- /**
221
- * Cancel all pending loads
222
- */
223
- const cancelPendingLoads = (): void => {
224
- const count = requestQueue.length;
225
- if (count > 0) {
226
- cancelledRequests += count;
227
- requestQueue = [];
228
- }
229
- };
230
-
231
- /**
232
- * Check if a range is already being loaded
233
- */
234
- const isRangeLoading = (range: ItemRange): boolean => {
235
- const rangeKey = getRangeKey(range);
236
- return activeRanges.has(rangeKey);
237
- };
238
-
239
- /**
240
- * Get loading statistics
241
- */
242
- const getStats = (): LoadingStats => {
243
- const stats = {
244
- pendingRequests: activeRequests,
245
- completedRequests,
246
- failedRequests,
247
- cancelledRequests,
248
- currentVelocity,
249
- canLoad: canLoad(),
250
- queuedRequests: requestQueue.length,
251
- };
252
-
253
- return stats;
254
- };
255
-
256
- return {
257
- requestLoad,
258
- updateVelocity,
259
- cancelPendingLoads,
260
- getStats,
261
- isRangeLoading,
262
- };
263
- };