mtrl-addons 0.1.1 → 0.2.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 (115) hide show
  1. package/build.js +139 -108
  2. package/package.json +13 -4
  3. package/scripts/debug/vlist-selection.ts +121 -0
  4. package/src/components/index.ts +5 -41
  5. package/src/components/{list → vlist}/config.ts +66 -95
  6. package/src/components/vlist/constants.ts +23 -0
  7. package/src/components/vlist/features/api.ts +322 -0
  8. package/src/components/vlist/features/index.ts +10 -0
  9. package/src/components/vlist/features/selection.ts +444 -0
  10. package/src/components/vlist/features/viewport.ts +65 -0
  11. package/src/components/vlist/index.ts +16 -0
  12. package/src/components/{list → vlist}/types.ts +104 -26
  13. package/src/components/vlist/vlist.ts +92 -0
  14. package/src/core/compose/features/gestures/index.ts +227 -0
  15. package/src/core/compose/features/gestures/longpress.ts +383 -0
  16. package/src/core/compose/features/gestures/pan.ts +424 -0
  17. package/src/core/compose/features/gestures/pinch.ts +475 -0
  18. package/src/core/compose/features/gestures/rotate.ts +485 -0
  19. package/src/core/compose/features/gestures/swipe.ts +492 -0
  20. package/src/core/compose/features/gestures/tap.ts +334 -0
  21. package/src/core/compose/features/index.ts +2 -38
  22. package/src/core/compose/index.ts +13 -29
  23. package/src/core/gestures/index.ts +31 -0
  24. package/src/core/gestures/longpress.ts +68 -0
  25. package/src/core/gestures/manager.ts +418 -0
  26. package/src/core/gestures/pan.ts +48 -0
  27. package/src/core/gestures/pinch.ts +58 -0
  28. package/src/core/gestures/rotate.ts +58 -0
  29. package/src/core/gestures/swipe.ts +66 -0
  30. package/src/core/gestures/tap.ts +45 -0
  31. package/src/core/gestures/types.ts +387 -0
  32. package/src/core/gestures/utils.ts +128 -0
  33. package/src/core/index.ts +27 -151
  34. package/src/core/layout/schema.ts +73 -35
  35. package/src/core/layout/types.ts +5 -2
  36. package/src/core/viewport/constants.ts +140 -0
  37. package/src/core/viewport/features/base.ts +73 -0
  38. package/src/core/viewport/features/collection.ts +882 -0
  39. package/src/core/viewport/features/events.ts +130 -0
  40. package/src/core/viewport/features/index.ts +20 -0
  41. package/src/core/{list-manager/features/viewport → viewport/features}/item-size.ts +27 -30
  42. package/src/core/{list-manager/features/viewport → viewport/features}/loading.ts +4 -4
  43. package/src/core/viewport/features/momentum.ts +260 -0
  44. package/src/core/viewport/features/placeholders.ts +335 -0
  45. package/src/core/viewport/features/rendering.ts +568 -0
  46. package/src/core/viewport/features/scrollbar.ts +434 -0
  47. package/src/core/viewport/features/scrolling.ts +618 -0
  48. package/src/core/viewport/features/utils.ts +88 -0
  49. package/src/core/viewport/features/virtual.ts +384 -0
  50. package/src/core/viewport/index.ts +31 -0
  51. package/src/core/viewport/types.ts +133 -0
  52. package/src/core/viewport/utils/speed-tracker.ts +79 -0
  53. package/src/core/viewport/viewport.ts +246 -0
  54. package/src/index.ts +0 -7
  55. package/src/styles/components/_vlist.scss +331 -0
  56. package/src/styles/index.scss +1 -1
  57. package/test/components/vlist-selection.test.ts +240 -0
  58. package/test/components/vlist.test.ts +63 -0
  59. package/test/core/collection/adapter.test.ts +161 -0
  60. package/bun.lock +0 -792
  61. package/src/components/list/api.ts +0 -314
  62. package/src/components/list/constants.ts +0 -56
  63. package/src/components/list/features/api.ts +0 -428
  64. package/src/components/list/features/index.ts +0 -31
  65. package/src/components/list/features/list-manager.ts +0 -502
  66. package/src/components/list/index.ts +0 -39
  67. package/src/components/list/list.ts +0 -234
  68. package/src/core/collection/base-collection.ts +0 -100
  69. package/src/core/collection/collection-composer.ts +0 -178
  70. package/src/core/collection/collection.ts +0 -745
  71. package/src/core/collection/constants.ts +0 -172
  72. package/src/core/collection/events.ts +0 -428
  73. package/src/core/collection/features/api/loading.ts +0 -279
  74. package/src/core/collection/features/operations/data-operations.ts +0 -147
  75. package/src/core/collection/index.ts +0 -104
  76. package/src/core/collection/state.ts +0 -497
  77. package/src/core/collection/types.ts +0 -404
  78. package/src/core/compose/features/collection.ts +0 -119
  79. package/src/core/compose/features/selection.ts +0 -213
  80. package/src/core/compose/features/styling.ts +0 -108
  81. package/src/core/list-manager/api.ts +0 -599
  82. package/src/core/list-manager/config.ts +0 -593
  83. package/src/core/list-manager/constants.ts +0 -268
  84. package/src/core/list-manager/features/api.ts +0 -58
  85. package/src/core/list-manager/features/collection/collection.ts +0 -705
  86. package/src/core/list-manager/features/collection/index.ts +0 -17
  87. package/src/core/list-manager/features/viewport/constants.ts +0 -42
  88. package/src/core/list-manager/features/viewport/index.ts +0 -16
  89. package/src/core/list-manager/features/viewport/placeholders.ts +0 -281
  90. package/src/core/list-manager/features/viewport/rendering.ts +0 -575
  91. package/src/core/list-manager/features/viewport/scrollbar.ts +0 -495
  92. package/src/core/list-manager/features/viewport/scrolling.ts +0 -795
  93. package/src/core/list-manager/features/viewport/template.ts +0 -220
  94. package/src/core/list-manager/features/viewport/viewport.ts +0 -654
  95. package/src/core/list-manager/features/viewport/virtual.ts +0 -309
  96. package/src/core/list-manager/index.ts +0 -279
  97. package/src/core/list-manager/list-manager.ts +0 -206
  98. package/src/core/list-manager/types.ts +0 -439
  99. package/src/core/list-manager/utils/calculations.ts +0 -290
  100. package/src/core/list-manager/utils/range-calculator.ts +0 -349
  101. package/src/core/list-manager/utils/speed-tracker.ts +0 -273
  102. package/src/styles/components/_list.scss +0 -244
  103. package/src/types/mtrl.d.ts +0 -6
  104. package/test/components/list.test.ts +0 -256
  105. package/test/core/collection/failed-ranges.test.ts +0 -270
  106. package/test/core/compose/features.test.ts +0 -183
  107. package/test/core/list-manager/features/collection.test.ts +0 -704
  108. package/test/core/list-manager/features/viewport.test.ts +0 -698
  109. package/test/core/list-manager/list-manager.test.ts +0 -593
  110. package/test/core/list-manager/utils/calculations.test.ts +0 -433
  111. package/test/core/list-manager/utils/range-calculator.test.ts +0 -569
  112. package/test/core/list-manager/utils/speed-tracker.test.ts +0 -530
  113. package/tsconfig.build.json +0 -14
  114. /package/src/components/{list → vlist}/features.ts +0 -0
  115. /package/src/core/{compose → viewport}/features/performance.ts +0 -0
@@ -1,569 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import {
3
- calculateRangeIndex,
4
- calculateRangeBounds,
5
- calculateRequiredRanges,
6
- calculatePrefetchRanges,
7
- calculateRangeResult,
8
- rangeToPaginationParams,
9
- rangesToBatchParams,
10
- calculateOptimalRangeSize,
11
- isRangeInViewport,
12
- calculateRangePriority,
13
- sortRangesByPriority,
14
- mergeAdjacentRanges,
15
- calculateRangeCleanupCandidates,
16
- calculateRangeLoadingOrder,
17
- getRangeDebugInfo,
18
- } from "../../../../src/core/list-manager/utils/range-calculator";
19
- import type { ItemRange } from "../../../../src/core/list-manager/types";
20
-
21
- describe("Range Calculator Utility", () => {
22
- describe("calculateRangeIndex", () => {
23
- it("should calculate range index from item index", () => {
24
- expect(calculateRangeIndex(0, 10)).toBe(0);
25
- expect(calculateRangeIndex(5, 10)).toBe(0);
26
- expect(calculateRangeIndex(10, 10)).toBe(1);
27
- expect(calculateRangeIndex(15, 10)).toBe(1);
28
- expect(calculateRangeIndex(25, 10)).toBe(2);
29
- });
30
-
31
- it("should handle edge cases", () => {
32
- expect(calculateRangeIndex(0, 1)).toBe(0);
33
- expect(calculateRangeIndex(100, 50)).toBe(2);
34
- });
35
- });
36
-
37
- describe("calculateRangeBounds", () => {
38
- it("should calculate range bounds", () => {
39
- const bounds = calculateRangeBounds(2, 10, 100);
40
-
41
- expect(bounds.start).toBe(20);
42
- expect(bounds.end).toBe(29);
43
- });
44
-
45
- it("should handle range at end of list", () => {
46
- const bounds = calculateRangeBounds(9, 10, 95);
47
-
48
- expect(bounds.start).toBe(90);
49
- expect(bounds.end).toBe(94); // Clamped to total items
50
- });
51
-
52
- it("should handle single item ranges", () => {
53
- const bounds = calculateRangeBounds(5, 1, 10);
54
-
55
- expect(bounds.start).toBe(5);
56
- expect(bounds.end).toBe(5);
57
- });
58
- });
59
-
60
- describe("calculateRequiredRanges", () => {
61
- it("should calculate required ranges for visible area", () => {
62
- const visibleRange: ItemRange = { start: 15, end: 35 };
63
- const loadedRanges = new Set([0, 1]); // Ranges 0-9, 10-19 loaded
64
-
65
- const required = calculateRequiredRanges({
66
- visibleRange,
67
- rangeSize: 10,
68
- totalItems: 100,
69
- loadedRanges,
70
- prefetchDistance: 1,
71
- });
72
-
73
- expect(required.length).toBeGreaterThan(0);
74
- expect(required.some((r) => r.start >= 15 && r.end <= 35)).toBe(true);
75
- });
76
-
77
- it("should include prefetch ranges", () => {
78
- const visibleRange: ItemRange = { start: 20, end: 30 };
79
- const loadedRanges = new Set<number>();
80
-
81
- const required = calculateRequiredRanges({
82
- visibleRange,
83
- rangeSize: 10,
84
- totalItems: 100,
85
- loadedRanges,
86
- prefetchDistance: 1,
87
- });
88
-
89
- // Should include ranges before and after visible range
90
- const allRanges = required.flatMap((r) =>
91
- Array.from({ length: r.end - r.start + 1 }, (_, i) => r.start + i)
92
- );
93
-
94
- expect(allRanges.some((i) => i < 20)).toBe(true); // Before visible
95
- expect(allRanges.some((i) => i > 30)).toBe(true); // After visible
96
- });
97
-
98
- it("should exclude already loaded ranges", () => {
99
- const visibleRange: ItemRange = { start: 20, end: 30 };
100
- const loadedRanges = new Set([2]); // Range 20-29 already loaded
101
-
102
- const required = calculateRequiredRanges({
103
- visibleRange,
104
- rangeSize: 10,
105
- totalItems: 100,
106
- loadedRanges,
107
- prefetchDistance: 0,
108
- });
109
-
110
- // Should not include range 2 (already loaded)
111
- expect(required.every((r) => !(r.start === 20 && r.end === 29))).toBe(
112
- true
113
- );
114
- });
115
- });
116
-
117
- describe("calculatePrefetchRanges", () => {
118
- it("should calculate prefetch ranges in both directions", () => {
119
- const visibleRange: ItemRange = { start: 40, end: 60 };
120
-
121
- const prefetch = calculatePrefetchRanges({
122
- visibleRange,
123
- direction: "forward",
124
- distance: 2,
125
- rangeSize: 20,
126
- totalItems: 200,
127
- });
128
-
129
- expect(prefetch.length).toBeGreaterThan(0);
130
- expect(prefetch.some((r) => r.start > 60)).toBe(true); // Forward ranges
131
- });
132
-
133
- it("should handle backward prefetch", () => {
134
- const visibleRange: ItemRange = { start: 40, end: 60 };
135
-
136
- const prefetch = calculatePrefetchRanges({
137
- visibleRange,
138
- direction: "backward",
139
- distance: 1,
140
- rangeSize: 20,
141
- totalItems: 200,
142
- });
143
-
144
- expect(prefetch.some((r) => r.end < 40)).toBe(true); // Backward ranges
145
- });
146
-
147
- it("should respect total items boundary", () => {
148
- const visibleRange: ItemRange = { start: 80, end: 99 };
149
-
150
- const prefetch = calculatePrefetchRanges({
151
- visibleRange,
152
- direction: "forward",
153
- distance: 5,
154
- rangeSize: 20,
155
- totalItems: 100,
156
- });
157
-
158
- // Should not exceed total items
159
- expect(prefetch.every((r) => r.end < 100)).toBe(true);
160
- });
161
- });
162
-
163
- describe("calculateRangeResult", () => {
164
- it("should calculate comprehensive range result", () => {
165
- const visibleRange: ItemRange = { start: 25, end: 45 };
166
- const loadedRanges = new Set([1, 2]); // Some ranges loaded
167
-
168
- const result = calculateRangeResult({
169
- visibleRange,
170
- rangeSize: 20,
171
- totalItems: 200,
172
- loadedRanges,
173
- prefetchDistance: 1,
174
- direction: "forward",
175
- });
176
-
177
- expect(result).toHaveProperty("required");
178
- expect(result).toHaveProperty("prefetch");
179
- expect(result).toHaveProperty("missing");
180
- expect(result).toHaveProperty("priority");
181
- expect(result).toHaveProperty("loadingOrder");
182
-
183
- expect(result.required.length).toBeGreaterThan(0);
184
- expect(result.priority.length).toBeGreaterThan(0);
185
- });
186
- });
187
-
188
- describe("rangeToPaginationParams", () => {
189
- it("should convert range to page-based pagination", () => {
190
- const range: ItemRange = { start: 20, end: 39 };
191
-
192
- const params = rangeToPaginationParams(range, "page", 10);
193
-
194
- expect(params.strategy).toBe("page");
195
- expect(params.page).toBe(3); // Page 3 for items 20-29
196
- expect(params.limit).toBe(20); // Range size
197
- });
198
-
199
- it("should convert range to offset-based pagination", () => {
200
- const range: ItemRange = { start: 50, end: 74 };
201
-
202
- const params = rangeToPaginationParams(range, "offset", 25);
203
-
204
- expect(params.strategy).toBe("offset");
205
- expect(params.offset).toBe(50);
206
- expect(params.limit).toBe(25);
207
- });
208
-
209
- it("should convert range to cursor-based pagination", () => {
210
- const range: ItemRange = { start: 100, end: 119 };
211
-
212
- const params = rangeToPaginationParams(range, "cursor", 20);
213
-
214
- expect(params.strategy).toBe("cursor");
215
- expect(params.after).toBe("item-99"); // Cursor after last item before range
216
- expect(params.limit).toBe(20);
217
- });
218
- });
219
-
220
- describe("rangesToBatchParams", () => {
221
- it("should convert multiple ranges to batch parameters", () => {
222
- const ranges: ItemRange[] = [
223
- { start: 0, end: 19 },
224
- { start: 40, end: 59 },
225
- { start: 80, end: 99 },
226
- ];
227
-
228
- const batch = rangesToBatchParams(ranges, "offset");
229
-
230
- expect(batch.strategy).toBe("batch");
231
- expect(batch.requests).toHaveLength(3);
232
- expect(batch.requests[0].offset).toBe(0);
233
- expect(batch.requests[1].offset).toBe(40);
234
- expect(batch.requests[2].offset).toBe(80);
235
- });
236
-
237
- it("should handle empty ranges", () => {
238
- const batch = rangesToBatchParams([], "page");
239
-
240
- expect(batch.requests).toHaveLength(0);
241
- });
242
- });
243
-
244
- describe("calculateOptimalRangeSize", () => {
245
- it("should calculate optimal range size based on parameters", () => {
246
- const size = calculateOptimalRangeSize({
247
- averageItemSize: 50,
248
- containerSize: 500,
249
- networkLatency: 100,
250
- bandwidth: 1000, // kb/s
251
- minRangeSize: 10,
252
- maxRangeSize: 100,
253
- });
254
-
255
- expect(size).toBeGreaterThanOrEqual(10);
256
- expect(size).toBeLessThanOrEqual(100);
257
- });
258
-
259
- it("should respect minimum range size", () => {
260
- const size = calculateOptimalRangeSize({
261
- averageItemSize: 10,
262
- containerSize: 100,
263
- networkLatency: 10,
264
- bandwidth: 10000,
265
- minRangeSize: 50,
266
- maxRangeSize: 200,
267
- });
268
-
269
- expect(size).toBeGreaterThanOrEqual(50);
270
- });
271
-
272
- it("should respect maximum range size", () => {
273
- const size = calculateOptimalRangeSize({
274
- averageItemSize: 200,
275
- containerSize: 2000,
276
- networkLatency: 500,
277
- bandwidth: 100,
278
- minRangeSize: 5,
279
- maxRangeSize: 30,
280
- });
281
-
282
- expect(size).toBeLessThanOrEqual(30);
283
- });
284
- });
285
-
286
- describe("isRangeInViewport", () => {
287
- it("should detect range in viewport", () => {
288
- const range: ItemRange = { start: 20, end: 40 };
289
- const viewport: ItemRange = { start: 10, end: 50 };
290
-
291
- expect(isRangeInViewport(range, viewport)).toBe(true);
292
- });
293
-
294
- it("should detect range partially in viewport", () => {
295
- const range: ItemRange = { start: 5, end: 25 };
296
- const viewport: ItemRange = { start: 20, end: 60 };
297
-
298
- expect(isRangeInViewport(range, viewport)).toBe(true);
299
- });
300
-
301
- it("should detect range outside viewport", () => {
302
- const range: ItemRange = { start: 100, end: 120 };
303
- const viewport: ItemRange = { start: 10, end: 50 };
304
-
305
- expect(isRangeInViewport(range, viewport)).toBe(false);
306
- });
307
-
308
- it("should handle edge cases", () => {
309
- const range: ItemRange = { start: 50, end: 70 };
310
- const viewport: ItemRange = { start: 70, end: 90 };
311
-
312
- expect(isRangeInViewport(range, viewport)).toBe(false); // Touching but not overlapping
313
- });
314
- });
315
-
316
- describe("calculateRangePriority", () => {
317
- it("should assign high priority to visible ranges", () => {
318
- const range: ItemRange = { start: 20, end: 40 };
319
- const viewport: ItemRange = { start: 15, end: 45 };
320
-
321
- const priority = calculateRangePriority({
322
- range,
323
- viewport,
324
- direction: "forward",
325
- loadingStrategy: "normal",
326
- });
327
-
328
- expect(priority).toBeGreaterThan(0.8); // High priority
329
- });
330
-
331
- it("should assign medium priority to prefetch ranges", () => {
332
- const range: ItemRange = { start: 50, end: 70 };
333
- const viewport: ItemRange = { start: 10, end: 30 };
334
-
335
- const priority = calculateRangePriority({
336
- range,
337
- viewport,
338
- direction: "forward",
339
- loadingStrategy: "normal",
340
- });
341
-
342
- expect(priority).toBeGreaterThan(0.3);
343
- expect(priority).toBeLessThan(0.8);
344
- });
345
-
346
- it("should assign lower priority to ranges in opposite direction", () => {
347
- const range: ItemRange = { start: 0, end: 20 };
348
- const viewport: ItemRange = { start: 50, end: 70 };
349
-
350
- const forwardPriority = calculateRangePriority({
351
- range,
352
- viewport,
353
- direction: "forward",
354
- loadingStrategy: "normal",
355
- });
356
-
357
- const backwardPriority = calculateRangePriority({
358
- range,
359
- viewport,
360
- direction: "backward",
361
- loadingStrategy: "normal",
362
- });
363
-
364
- expect(backwardPriority).toBeGreaterThan(forwardPriority);
365
- });
366
- });
367
-
368
- describe("sortRangesByPriority", () => {
369
- it("should sort ranges by priority", () => {
370
- const ranges: ItemRange[] = [
371
- { start: 100, end: 120 }, // Far from viewport
372
- { start: 20, end: 40 }, // In viewport
373
- { start: 50, end: 70 }, // Near viewport
374
- ];
375
- const viewport: ItemRange = { start: 15, end: 35 };
376
-
377
- const sorted = sortRangesByPriority({
378
- ranges,
379
- viewport,
380
- direction: "forward",
381
- loadingStrategy: "normal",
382
- });
383
-
384
- // First range should be the one in viewport
385
- expect(sorted[0].start).toBe(20);
386
- expect(sorted[0].end).toBe(40);
387
- });
388
- });
389
-
390
- describe("mergeAdjacentRanges", () => {
391
- it("should merge adjacent ranges", () => {
392
- const ranges: ItemRange[] = [
393
- { start: 0, end: 19 },
394
- { start: 20, end: 39 },
395
- { start: 40, end: 59 },
396
- { start: 80, end: 99 }, // Gap, should not merge
397
- ];
398
-
399
- const merged = mergeAdjacentRanges(ranges);
400
-
401
- expect(merged).toHaveLength(2);
402
- expect(merged[0]).toEqual({ start: 0, end: 59 });
403
- expect(merged[1]).toEqual({ start: 80, end: 99 });
404
- });
405
-
406
- it("should handle overlapping ranges", () => {
407
- const ranges: ItemRange[] = [
408
- { start: 0, end: 25 },
409
- { start: 20, end: 45 },
410
- { start: 40, end: 60 },
411
- ];
412
-
413
- const merged = mergeAdjacentRanges(ranges);
414
-
415
- expect(merged).toHaveLength(1);
416
- expect(merged[0]).toEqual({ start: 0, end: 60 });
417
- });
418
-
419
- it("should handle non-adjacent ranges", () => {
420
- const ranges: ItemRange[] = [
421
- { start: 0, end: 10 },
422
- { start: 20, end: 30 },
423
- { start: 40, end: 50 },
424
- ];
425
-
426
- const merged = mergeAdjacentRanges(ranges);
427
-
428
- expect(merged).toHaveLength(3); // No merging
429
- expect(merged).toEqual(ranges);
430
- });
431
- });
432
-
433
- describe("calculateRangeCleanupCandidates", () => {
434
- it("should identify ranges for cleanup", () => {
435
- const loadedRanges: ItemRange[] = [
436
- { start: 0, end: 19 }, // Far from viewport
437
- { start: 20, end: 39 }, // In viewport
438
- { start: 40, end: 59 }, // Near viewport
439
- { start: 100, end: 119 }, // Very far from viewport
440
- ];
441
- const viewport: ItemRange = { start: 25, end: 45 };
442
-
443
- const candidates = calculateRangeCleanupCandidates({
444
- loadedRanges,
445
- viewport,
446
- maxDistance: 50,
447
- keepMinimum: 2,
448
- });
449
-
450
- expect(candidates.length).toBeGreaterThan(0);
451
- expect(candidates.some((r) => r.start === 100)).toBe(true); // Far range should be candidate
452
- });
453
-
454
- it("should respect minimum keep count", () => {
455
- const loadedRanges: ItemRange[] = [
456
- { start: 0, end: 19 },
457
- { start: 200, end: 219 },
458
- ];
459
- const viewport: ItemRange = { start: 100, end: 120 };
460
-
461
- const candidates = calculateRangeCleanupCandidates({
462
- loadedRanges,
463
- viewport,
464
- maxDistance: 10,
465
- keepMinimum: 2,
466
- });
467
-
468
- expect(candidates.length).toBe(0); // Should keep all due to minimum
469
- });
470
- });
471
-
472
- describe("calculateRangeLoadingOrder", () => {
473
- it("should determine optimal loading order", () => {
474
- const ranges: ItemRange[] = [
475
- { start: 0, end: 19 }, // Far
476
- { start: 20, end: 39 }, // Visible
477
- { start: 40, end: 59 }, // Adjacent
478
- { start: 100, end: 119 }, // Very far
479
- ];
480
- const viewport: ItemRange = { start: 25, end: 35 };
481
-
482
- const order = calculateRangeLoadingOrder({
483
- ranges,
484
- viewport,
485
- direction: "forward",
486
- maxConcurrent: 2,
487
- loadingStrategy: "normal",
488
- });
489
-
490
- expect(order.length).toBeGreaterThan(0);
491
- expect(order[0].priority).toBeGreaterThan(
492
- order[order.length - 1].priority
493
- );
494
- });
495
-
496
- it("should group concurrent loads", () => {
497
- const ranges: ItemRange[] = [
498
- { start: 0, end: 19 },
499
- { start: 20, end: 39 },
500
- { start: 40, end: 59 },
501
- { start: 60, end: 79 },
502
- ];
503
- const viewport: ItemRange = { start: 25, end: 35 };
504
-
505
- const order = calculateRangeLoadingOrder({
506
- ranges,
507
- viewport,
508
- direction: "forward",
509
- maxConcurrent: 2,
510
- loadingStrategy: "aggressive",
511
- });
512
-
513
- // Should group some ranges for concurrent loading
514
- expect(order.some((item) => item.concurrentGroup !== undefined)).toBe(
515
- true
516
- );
517
- });
518
- });
519
-
520
- describe("getRangeDebugInfo", () => {
521
- it("should provide comprehensive debug information", () => {
522
- const ranges: ItemRange[] = [
523
- { start: 0, end: 19 },
524
- { start: 20, end: 39 },
525
- { start: 60, end: 79 },
526
- ];
527
- const viewport: ItemRange = { start: 25, end: 45 };
528
- const loadedRanges = new Set([1]);
529
-
530
- const debug = getRangeDebugInfo({
531
- ranges,
532
- viewport,
533
- loadedRanges,
534
- rangeSize: 20,
535
- totalItems: 200,
536
- });
537
-
538
- expect(debug).toHaveProperty("totalRanges");
539
- expect(debug).toHaveProperty("loadedCount");
540
- expect(debug).toHaveProperty("visibleRanges");
541
- expect(debug).toHaveProperty("coverage");
542
- expect(debug).toHaveProperty("efficiency");
543
- expect(debug).toHaveProperty("gaps");
544
- expect(debug).toHaveProperty("suggestions");
545
-
546
- expect(debug.totalRanges).toBe(3);
547
- expect(debug.loadedCount).toBe(1);
548
- expect(debug.visibleRanges).toBeGreaterThan(0);
549
- });
550
-
551
- it("should calculate coverage percentage", () => {
552
- const ranges: ItemRange[] = [
553
- { start: 0, end: 49 }, // 50 items
554
- ];
555
- const viewport: ItemRange = { start: 0, end: 99 }; // 100 items needed
556
- const loadedRanges = new Set([0]);
557
-
558
- const debug = getRangeDebugInfo({
559
- ranges,
560
- viewport,
561
- loadedRanges,
562
- rangeSize: 50,
563
- totalItems: 200,
564
- });
565
-
566
- expect(debug.coverage).toBeCloseTo(0.5, 1); // 50% coverage
567
- });
568
- });
569
- });