@pdanpdan/virtual-scroll 0.2.1 → 0.3.0
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.
- package/README.md +182 -88
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +100 -35
- package/dist/index.js +1 -844
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +902 -0
- package/dist/index.mjs.map +1 -0
- package/dist/virtual-scroll.css +2 -0
- package/package.json +8 -5
- package/src/components/VirtualScroll.test.ts +62 -15
- package/src/components/VirtualScroll.vue +83 -19
- package/src/composables/useVirtualScroll.test.ts +501 -68
- package/src/composables/useVirtualScroll.ts +150 -65
- package/dist/index.css +0 -2
|
@@ -117,11 +117,11 @@ describe('useVirtualScroll', () => {
|
|
|
117
117
|
|
|
118
118
|
it('should recalculate when gaps change', async () => {
|
|
119
119
|
const { result, props } = setup({ ...defaultProps, gap: 10 });
|
|
120
|
-
expect(result.totalHeight.value).toBe(
|
|
120
|
+
expect(result.totalHeight.value).toBe(5990); // 100 * (50 + 10) - 10
|
|
121
121
|
|
|
122
122
|
props.value.gap = 20;
|
|
123
123
|
await nextTick();
|
|
124
|
-
expect(result.totalHeight.value).toBe(
|
|
124
|
+
expect(result.totalHeight.value).toBe(6980); // 100 * (50 + 20) - 20
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
it('should handle itemSize as a function', async () => {
|
|
@@ -130,6 +130,7 @@ describe('useVirtualScroll', () => {
|
|
|
130
130
|
itemSize: (_item: { id: number; }, index: number) => 50 + index,
|
|
131
131
|
});
|
|
132
132
|
// 50*100 + (0+99)*100/2 = 5000 + 4950 = 9950
|
|
133
|
+
// 9950 - gap(0) = 9950
|
|
133
134
|
expect(result.totalHeight.value).toBe(9950);
|
|
134
135
|
});
|
|
135
136
|
|
|
@@ -140,13 +141,13 @@ describe('useVirtualScroll', () => {
|
|
|
140
141
|
columnCount: 10,
|
|
141
142
|
columnWidth: 100,
|
|
142
143
|
});
|
|
143
|
-
expect(result.totalWidth.value).toBe(1000);
|
|
144
|
-
expect(result.totalHeight.value).toBe(5000);
|
|
144
|
+
expect(result.totalWidth.value).toBe(1000); // 10 * 100 - 0
|
|
145
|
+
expect(result.totalHeight.value).toBe(5000); // 100 * 50 - 0
|
|
145
146
|
});
|
|
146
147
|
|
|
147
148
|
it('should handle horizontal direction', async () => {
|
|
148
149
|
const { result } = setup({ ...defaultProps, direction: 'horizontal' });
|
|
149
|
-
expect(result.totalWidth.value).toBe(5000);
|
|
150
|
+
expect(result.totalWidth.value).toBe(5000); // 100 * 50 - 0
|
|
150
151
|
expect(result.totalHeight.value).toBe(0);
|
|
151
152
|
});
|
|
152
153
|
|
|
@@ -238,46 +239,46 @@ describe('useVirtualScroll', () => {
|
|
|
238
239
|
// Horizontal
|
|
239
240
|
const { result: rH } = setup({ ...defaultProps, direction: 'horizontal', itemSize: undefined });
|
|
240
241
|
await nextTick();
|
|
241
|
-
// Estimate is
|
|
242
|
-
rH.updateItemSizes([ { index: 0, inlineSize:
|
|
242
|
+
// Estimate is 40 (new DEFAULT_ITEM_SIZE). Update with 30.
|
|
243
|
+
rH.updateItemSizes([ { index: 0, inlineSize: 30, blockSize: 30 } ]);
|
|
243
244
|
await nextTick();
|
|
244
|
-
expect(rH.renderedItems.value[ 0 ]?.size.width).toBe(
|
|
245
|
+
expect(rH.renderedItems.value[ 0 ]?.size.width).toBe(30);
|
|
245
246
|
|
|
246
|
-
// Subsequent update with smaller size should be
|
|
247
|
-
rH.updateItemSizes([ { index: 0, inlineSize:
|
|
247
|
+
// Subsequent update with smaller size should also be applied now
|
|
248
|
+
rH.updateItemSizes([ { index: 0, inlineSize: 25, blockSize: 25 } ]);
|
|
248
249
|
await nextTick();
|
|
249
|
-
expect(rH.renderedItems.value[ 0 ]?.size.width).toBe(
|
|
250
|
+
expect(rH.renderedItems.value[ 0 ]?.size.width).toBe(25);
|
|
250
251
|
|
|
251
252
|
// Vertical
|
|
252
253
|
const { result: rV } = setup({ ...defaultProps, direction: 'vertical', itemSize: undefined });
|
|
253
254
|
await nextTick();
|
|
254
|
-
rV.updateItemSizes([ { index: 0, inlineSize:
|
|
255
|
+
rV.updateItemSizes([ { index: 0, inlineSize: 30, blockSize: 30 } ]);
|
|
255
256
|
await nextTick();
|
|
256
|
-
expect(rV.renderedItems.value[ 0 ]?.size.height).toBe(
|
|
257
|
+
expect(rV.renderedItems.value[ 0 ]?.size.height).toBe(30);
|
|
257
258
|
|
|
258
|
-
// Subsequent update with smaller size should be
|
|
259
|
-
rV.updateItemSizes([ { index: 0, inlineSize:
|
|
259
|
+
// Subsequent update with smaller size should be applied
|
|
260
|
+
rV.updateItemSizes([ { index: 0, inlineSize: 20, blockSize: 20 } ]);
|
|
260
261
|
await nextTick();
|
|
261
|
-
expect(rV.renderedItems.value[ 0 ]?.size.height).toBe(
|
|
262
|
+
expect(rV.renderedItems.value[ 0 ]?.size.height).toBe(20);
|
|
262
263
|
});
|
|
263
264
|
|
|
264
265
|
it('should handle updateItemSize and trigger reactivity', async () => {
|
|
265
266
|
const { result } = setup({ ...defaultProps, itemSize: undefined });
|
|
266
|
-
expect(result.totalHeight.value).toBe(
|
|
267
|
+
expect(result.totalHeight.value).toBe(4000); // 100 * 40
|
|
267
268
|
|
|
268
269
|
result.updateItemSize(0, 100, 100);
|
|
269
270
|
await nextTick();
|
|
270
|
-
expect(result.totalHeight.value).toBe(
|
|
271
|
+
expect(result.totalHeight.value).toBe(4060); // 4000 - 40 + 100
|
|
271
272
|
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(100);
|
|
272
273
|
});
|
|
273
274
|
|
|
274
275
|
it('should treat 0, null, undefined as dynamic itemSize', async () => {
|
|
275
276
|
for (const val of [ 0, null, undefined ]) {
|
|
276
277
|
const { result } = setup({ ...defaultProps, itemSize: val as unknown as undefined });
|
|
277
|
-
expect(result.totalHeight.value).toBe(
|
|
278
|
+
expect(result.totalHeight.value).toBe(4000);
|
|
278
279
|
result.updateItemSize(0, 100, 100);
|
|
279
280
|
await nextTick();
|
|
280
|
-
expect(result.totalHeight.value).toBe(
|
|
281
|
+
expect(result.totalHeight.value).toBe(4060);
|
|
281
282
|
}
|
|
282
283
|
});
|
|
283
284
|
|
|
@@ -289,7 +290,7 @@ describe('useVirtualScroll', () => {
|
|
|
289
290
|
columnCount: 2,
|
|
290
291
|
columnWidth: val as unknown as undefined,
|
|
291
292
|
});
|
|
292
|
-
expect(result.getColumnWidth(0)).toBe(
|
|
293
|
+
expect(result.getColumnWidth(0)).toBe(100); // DEFAULT_COLUMN_WIDTH
|
|
293
294
|
const parent = document.createElement('div');
|
|
294
295
|
const col0 = document.createElement('div');
|
|
295
296
|
Object.defineProperty(col0, 'offsetWidth', { value: 200, configurable: true });
|
|
@@ -297,7 +298,7 @@ describe('useVirtualScroll', () => {
|
|
|
297
298
|
parent.appendChild(col0);
|
|
298
299
|
result.updateItemSize(0, 200, 50, parent);
|
|
299
300
|
await nextTick();
|
|
300
|
-
expect(result.totalWidth.value).toBe(
|
|
301
|
+
expect(result.totalWidth.value).toBe(300); // 200 + 100
|
|
301
302
|
}
|
|
302
303
|
});
|
|
303
304
|
|
|
@@ -345,14 +346,21 @@ describe('useVirtualScroll', () => {
|
|
|
345
346
|
expect(result.totalWidth.value).toBe(10 * 200); // 10 columns * 200 defaultColumnWidth
|
|
346
347
|
});
|
|
347
348
|
|
|
348
|
-
it('should ignore small delta updates in updateItemSize', async () => {
|
|
349
|
+
it('should ignore small delta updates in updateItemSize only after first measurement', async () => {
|
|
349
350
|
const { result } = setup({ ...defaultProps, itemSize: undefined });
|
|
350
|
-
|
|
351
|
+
// Default is 40. 40.1 is < 0.5 delta.
|
|
352
|
+
// First measurement should be accepted even if small delta from estimate.
|
|
353
|
+
result.updateItemSize(0, 40.1, 40.1);
|
|
351
354
|
await nextTick();
|
|
352
|
-
expect(result.
|
|
355
|
+
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(40.1);
|
|
356
|
+
|
|
357
|
+
// Second measurement with small delta from first should be ignored.
|
|
358
|
+
result.updateItemSize(0, 40.2, 40.2);
|
|
359
|
+
await nextTick();
|
|
360
|
+
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(40.1);
|
|
353
361
|
});
|
|
354
362
|
|
|
355
|
-
it('should
|
|
363
|
+
it('should update item height in both mode now (allow decreases)', async () => {
|
|
356
364
|
const { result } = setup({ ...defaultProps, direction: 'both', itemSize: undefined, columnCount: 2 });
|
|
357
365
|
result.updateItemSize(0, 100, 100);
|
|
358
366
|
await nextTick();
|
|
@@ -360,7 +368,7 @@ describe('useVirtualScroll', () => {
|
|
|
360
368
|
|
|
361
369
|
result.updateItemSize(0, 100, 80);
|
|
362
370
|
await nextTick();
|
|
363
|
-
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(
|
|
371
|
+
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(80);
|
|
364
372
|
});
|
|
365
373
|
|
|
366
374
|
it('should update item height in vertical mode', async () => {
|
|
@@ -368,26 +376,30 @@ describe('useVirtualScroll', () => {
|
|
|
368
376
|
result.updateItemSize(0, 100, 100);
|
|
369
377
|
await nextTick();
|
|
370
378
|
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(100);
|
|
379
|
+
|
|
380
|
+
result.updateItemSize(0, 100, 70);
|
|
381
|
+
await nextTick();
|
|
382
|
+
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(70);
|
|
371
383
|
});
|
|
372
384
|
|
|
373
385
|
it('should handle updateItemSize for horizontal direction', async () => {
|
|
374
386
|
const { result } = setup({ ...defaultProps, direction: 'horizontal', itemSize: undefined });
|
|
375
387
|
result.updateItemSize(0, 100, 50);
|
|
376
388
|
await nextTick();
|
|
377
|
-
expect(result.totalWidth.value).toBe(
|
|
389
|
+
expect(result.totalWidth.value).toBe(4060); // 4000 - 40 + 100
|
|
378
390
|
});
|
|
379
391
|
|
|
380
392
|
it('should preserve measurements in initializeSizes when dynamic', async () => {
|
|
381
393
|
const { result, props } = setup({ ...defaultProps, itemSize: undefined });
|
|
382
394
|
result.updateItemSize(0, 100, 100);
|
|
383
395
|
await nextTick();
|
|
384
|
-
expect(result.totalHeight.value).toBe(
|
|
396
|
+
expect(result.totalHeight.value).toBe(4060);
|
|
385
397
|
|
|
386
398
|
// Trigger initializeSizes by changing length
|
|
387
399
|
props.value.items = Array.from({ length: 101 }, (_, i) => ({ id: i }));
|
|
388
400
|
await nextTick();
|
|
389
|
-
// Should still be 100 for index 0, not reset to default
|
|
390
|
-
expect(result.totalHeight.value).toBe(
|
|
401
|
+
// Should still be 100 for index 0, not reset to default 40
|
|
402
|
+
expect(result.totalHeight.value).toBe(4060 + 40);
|
|
391
403
|
});
|
|
392
404
|
});
|
|
393
405
|
|
|
@@ -398,10 +410,10 @@ describe('useVirtualScroll', () => {
|
|
|
398
410
|
const { result } = setup({ ...defaultProps, container, direction: 'horizontal', itemSize: undefined });
|
|
399
411
|
await nextTick();
|
|
400
412
|
|
|
401
|
-
// index 10. itemSize is
|
|
413
|
+
// index 10. itemSize is 40 by default. totalWidth = 4000.
|
|
402
414
|
result.scrollToIndex(null, 10, { align: 'start', behavior: 'auto' });
|
|
403
415
|
await nextTick();
|
|
404
|
-
expect(result.scrollDetails.value.scrollOffset.x).toBe(
|
|
416
|
+
expect(result.scrollDetails.value.scrollOffset.x).toBe(400);
|
|
405
417
|
});
|
|
406
418
|
|
|
407
419
|
it('should handle scrollToIndex with window fallback when container is missing', async () => {
|
|
@@ -858,7 +870,7 @@ describe('useVirtualScroll', () => {
|
|
|
858
870
|
columnCount: 2,
|
|
859
871
|
columnWidth: [ 0 ] as unknown as number[],
|
|
860
872
|
});
|
|
861
|
-
expect(result.getColumnWidth(0)).toBe(
|
|
873
|
+
expect(result.getColumnWidth(0)).toBe(100); // DEFAULT_COLUMN_WIDTH
|
|
862
874
|
});
|
|
863
875
|
|
|
864
876
|
it('should handle columnWidth as a function', async () => {
|
|
@@ -869,7 +881,7 @@ describe('useVirtualScroll', () => {
|
|
|
869
881
|
columnWidth: (index: number) => (index % 2 === 0 ? 100 : 200),
|
|
870
882
|
});
|
|
871
883
|
expect(result.getColumnWidth(0)).toBe(100);
|
|
872
|
-
expect(result.totalWidth.value).toBe(1500);
|
|
884
|
+
expect(result.totalWidth.value).toBe(1500); // 5*100 + 5*200 - 0
|
|
873
885
|
});
|
|
874
886
|
|
|
875
887
|
it('should handle getColumnWidth fallback when dynamic', async () => {
|
|
@@ -879,7 +891,7 @@ describe('useVirtualScroll', () => {
|
|
|
879
891
|
columnCount: 2,
|
|
880
892
|
columnWidth: undefined,
|
|
881
893
|
});
|
|
882
|
-
expect(result.getColumnWidth(0)).toBe(
|
|
894
|
+
expect(result.getColumnWidth(0)).toBe(100);
|
|
883
895
|
});
|
|
884
896
|
|
|
885
897
|
it('should handle columnRange while loop coverage', async () => {
|
|
@@ -961,7 +973,6 @@ describe('useVirtualScroll', () => {
|
|
|
961
973
|
const { result } = setup({ ...defaultProps, container, stickyIndices: [ 0, 10 ], itemSize: 50 });
|
|
962
974
|
// We need to trigger scroll to update scrollY
|
|
963
975
|
container.dispatchEvent(new Event('scroll'));
|
|
964
|
-
await nextTick();
|
|
965
976
|
|
|
966
977
|
const item0 = result.renderedItems.value.find((i) => i.index === 0);
|
|
967
978
|
expect(item0!.offset.y).toBeLessThanOrEqual(450);
|
|
@@ -981,7 +992,6 @@ describe('useVirtualScroll', () => {
|
|
|
981
992
|
columnGap: 0,
|
|
982
993
|
});
|
|
983
994
|
container.dispatchEvent(new Event('scroll'));
|
|
984
|
-
await nextTick();
|
|
985
995
|
|
|
986
996
|
const item0 = result.renderedItems.value.find((i) => i.index === 0);
|
|
987
997
|
expect(item0!.offset.x).toBeLessThanOrEqual(450);
|
|
@@ -990,7 +1000,7 @@ describe('useVirtualScroll', () => {
|
|
|
990
1000
|
it('should handle dynamic sticky item pushing in vertical mode', async () => {
|
|
991
1001
|
const container = document.createElement('div');
|
|
992
1002
|
Object.defineProperty(container, 'clientHeight', { value: 500 });
|
|
993
|
-
Object.defineProperty(container, 'scrollTop', { value:
|
|
1003
|
+
Object.defineProperty(container, 'scrollTop', { value: 380, writable: true });
|
|
994
1004
|
|
|
995
1005
|
const { result } = setup({
|
|
996
1006
|
...defaultProps,
|
|
@@ -998,22 +1008,21 @@ describe('useVirtualScroll', () => {
|
|
|
998
1008
|
itemSize: undefined, // dynamic
|
|
999
1009
|
stickyIndices: [ 0, 10 ],
|
|
1000
1010
|
});
|
|
1001
|
-
await nextTick();
|
|
1002
1011
|
|
|
1003
1012
|
// Item 0 is sticky. Item 10 is next sticky.
|
|
1004
|
-
// Default size =
|
|
1005
|
-
// nextStickyY = itemSizesY.query(10) =
|
|
1006
|
-
// distance =
|
|
1007
|
-
//
|
|
1008
|
-
// stickyOffset.y = -(
|
|
1013
|
+
// Default size = 40.
|
|
1014
|
+
// nextStickyY = itemSizesY.query(10) = 400.
|
|
1015
|
+
// distance = 400 - 380 = 20.
|
|
1016
|
+
// 20 < 40 (item 0 height), so it should be pushed.
|
|
1017
|
+
// stickyOffset.y = -(40 - 20) = -20.
|
|
1009
1018
|
const stickyItem = result.renderedItems.value.find((i) => i.index === 0);
|
|
1010
|
-
expect(stickyItem?.stickyOffset.y).toBe(-
|
|
1019
|
+
expect(stickyItem?.stickyOffset.y).toBe(-20);
|
|
1011
1020
|
});
|
|
1012
1021
|
|
|
1013
1022
|
it('should handle dynamic sticky item pushing in horizontal mode', async () => {
|
|
1014
1023
|
const container = document.createElement('div');
|
|
1015
1024
|
Object.defineProperty(container, 'clientWidth', { value: 500 });
|
|
1016
|
-
Object.defineProperty(container, 'scrollLeft', { value:
|
|
1025
|
+
Object.defineProperty(container, 'scrollLeft', { value: 380, writable: true });
|
|
1017
1026
|
|
|
1018
1027
|
const { result } = setup({
|
|
1019
1028
|
...defaultProps,
|
|
@@ -1022,13 +1031,12 @@ describe('useVirtualScroll', () => {
|
|
|
1022
1031
|
itemSize: undefined, // dynamic
|
|
1023
1032
|
stickyIndices: [ 0, 10 ],
|
|
1024
1033
|
});
|
|
1025
|
-
await nextTick();
|
|
1026
1034
|
|
|
1027
|
-
// nextStickyX = itemSizesX.query(10) =
|
|
1028
|
-
// distance =
|
|
1029
|
-
//
|
|
1035
|
+
// nextStickyX = itemSizesX.query(10) = 400.
|
|
1036
|
+
// distance = 400 - 380 = 20.
|
|
1037
|
+
// 20 < 40, so stickyOffset.x = -20.
|
|
1030
1038
|
const stickyItem = result.renderedItems.value.find((i) => i.index === 0);
|
|
1031
|
-
expect(stickyItem?.stickyOffset.x).toBe(-
|
|
1039
|
+
expect(stickyItem?.stickyOffset.x).toBe(-20);
|
|
1032
1040
|
});
|
|
1033
1041
|
});
|
|
1034
1042
|
|
|
@@ -1150,7 +1158,7 @@ describe('useVirtualScroll', () => {
|
|
|
1150
1158
|
const { props } = setup({ ...defaultProps, items, container, restoreScrollOnPrepend: true });
|
|
1151
1159
|
await nextTick();
|
|
1152
1160
|
|
|
1153
|
-
const newItems = [ { id: -1 }, { id: 9999 } ];
|
|
1161
|
+
const newItems = [ { id: -1 }, { id: 9999 } ];
|
|
1154
1162
|
props.value.items = newItems;
|
|
1155
1163
|
await nextTick();
|
|
1156
1164
|
await nextTick();
|
|
@@ -1162,9 +1170,9 @@ describe('useVirtualScroll', () => {
|
|
|
1162
1170
|
Object.defineProperty(container, 'clientHeight', { value: 500, configurable: true });
|
|
1163
1171
|
Object.defineProperty(container, 'scrollHeight', { value: 5000, configurable: true });
|
|
1164
1172
|
const { result, props } = setup({ ...defaultProps, container, restoreScrollOnPrepend: true });
|
|
1165
|
-
|
|
1166
|
-
// pendingScroll should be set because it's not reached yet
|
|
1173
|
+
await nextTick();
|
|
1167
1174
|
|
|
1175
|
+
result.scrollToIndex(10, null, { behavior: 'smooth' });
|
|
1168
1176
|
props.value.items = [ { id: -1 }, ...props.value.items ];
|
|
1169
1177
|
await nextTick();
|
|
1170
1178
|
});
|
|
@@ -1234,11 +1242,11 @@ describe('useVirtualScroll', () => {
|
|
|
1234
1242
|
const { result } = setup({ ...defaultProps, itemSize: 0 });
|
|
1235
1243
|
result.updateItemSize(0, 100, 100);
|
|
1236
1244
|
await nextTick();
|
|
1237
|
-
expect(result.totalHeight.value).toBe(
|
|
1245
|
+
expect(result.totalHeight.value).toBe(4060);
|
|
1238
1246
|
|
|
1239
1247
|
result.refresh();
|
|
1240
1248
|
await nextTick();
|
|
1241
|
-
expect(result.totalHeight.value).toBe(
|
|
1249
|
+
expect(result.totalHeight.value).toBe(4000);
|
|
1242
1250
|
});
|
|
1243
1251
|
|
|
1244
1252
|
it('should trigger scroll correction on tree update with string alignment', async () => {
|
|
@@ -1286,6 +1294,78 @@ describe('useVirtualScroll', () => {
|
|
|
1286
1294
|
expect(result.scrollDetails.value.isScrolling).toBe(false);
|
|
1287
1295
|
vi.useRealTimers();
|
|
1288
1296
|
});
|
|
1297
|
+
|
|
1298
|
+
it('should update totals when function-based itemSize dependencies change and refresh is called', async () => {
|
|
1299
|
+
const defaultHeight = ref(50);
|
|
1300
|
+
const getRowHeight = () => defaultHeight.value;
|
|
1301
|
+
|
|
1302
|
+
const propsValue = ref({
|
|
1303
|
+
items: mockItems,
|
|
1304
|
+
direction: 'vertical' as const,
|
|
1305
|
+
itemSize: getRowHeight,
|
|
1306
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1307
|
+
|
|
1308
|
+
const result = useVirtualScroll(propsValue);
|
|
1309
|
+
expect(result.totalHeight.value).toBe(5000); // 100 * 50
|
|
1310
|
+
|
|
1311
|
+
defaultHeight.value = 60;
|
|
1312
|
+
// Total height should still be 5000 because getRowHeight reference didn't change
|
|
1313
|
+
// and initializeSizes hasn't been called automatically.
|
|
1314
|
+
expect(result.totalHeight.value).toBe(5000);
|
|
1315
|
+
|
|
1316
|
+
result.refresh();
|
|
1317
|
+
await nextTick();
|
|
1318
|
+
expect(result.totalHeight.value).toBe(6000);
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
it('should update totals via measurements even if itemSize is a function', async () => {
|
|
1322
|
+
const getRowHeight = () => 50;
|
|
1323
|
+
|
|
1324
|
+
const propsValue = ref({
|
|
1325
|
+
items: mockItems,
|
|
1326
|
+
direction: 'vertical' as const,
|
|
1327
|
+
itemSize: getRowHeight,
|
|
1328
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1329
|
+
|
|
1330
|
+
const result = useVirtualScroll(propsValue);
|
|
1331
|
+
expect(result.totalHeight.value).toBe(5000);
|
|
1332
|
+
|
|
1333
|
+
// Simulate ResizeObserver measurement
|
|
1334
|
+
result.updateItemSizes([ { index: 0, inlineSize: 100, blockSize: 70 } ]);
|
|
1335
|
+
await nextTick();
|
|
1336
|
+
|
|
1337
|
+
// Item 0 is now 70 instead of 50. Total: 50 * 99 + 70 = 4950 + 70 = 5020.
|
|
1338
|
+
expect(result.totalHeight.value).toBe(5020);
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1341
|
+
it('should update totals via measurements even if columnWidth is a function', async () => {
|
|
1342
|
+
const getColWidth = () => 100;
|
|
1343
|
+
|
|
1344
|
+
const propsValue = ref({
|
|
1345
|
+
items: mockItems,
|
|
1346
|
+
direction: 'both' as const,
|
|
1347
|
+
columnCount: 5,
|
|
1348
|
+
columnWidth: getColWidth,
|
|
1349
|
+
itemSize: 50,
|
|
1350
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1351
|
+
|
|
1352
|
+
const result = useVirtualScroll(propsValue);
|
|
1353
|
+
expect(result.totalWidth.value).toBe(500);
|
|
1354
|
+
|
|
1355
|
+
// Simulate ResizeObserver measurement on a cell
|
|
1356
|
+
// We need to provide an element with data-col-index
|
|
1357
|
+
const element = document.createElement('div');
|
|
1358
|
+
const cell = document.createElement('div');
|
|
1359
|
+
cell.dataset.colIndex = '0';
|
|
1360
|
+
Object.defineProperty(cell, 'offsetWidth', { value: 120 });
|
|
1361
|
+
element.appendChild(cell);
|
|
1362
|
+
|
|
1363
|
+
result.updateItemSizes([ { index: 0, inlineSize: 120, blockSize: 50, element } ]);
|
|
1364
|
+
await nextTick();
|
|
1365
|
+
|
|
1366
|
+
// Column 0 is now 120 instead of 100. Total: 100 * 4 + 120 = 520.
|
|
1367
|
+
expect(result.totalWidth.value).toBe(520);
|
|
1368
|
+
});
|
|
1289
1369
|
});
|
|
1290
1370
|
|
|
1291
1371
|
// eslint-disable-next-line test/prefer-lowercase-title
|
|
@@ -1307,7 +1387,7 @@ describe('useVirtualScroll', () => {
|
|
|
1307
1387
|
direction: 'both',
|
|
1308
1388
|
columnCount: 20,
|
|
1309
1389
|
columnWidth: 100,
|
|
1310
|
-
ssrRange: { start: 0, end: 10, colStart: 1, colEnd: 2 },
|
|
1390
|
+
ssrRange: { start: 0, end: 10, colStart: 1, colEnd: 2 },
|
|
1311
1391
|
initialScrollIndex: 0,
|
|
1312
1392
|
});
|
|
1313
1393
|
|
|
@@ -1416,8 +1496,8 @@ describe('useVirtualScroll', () => {
|
|
|
1416
1496
|
ssrRange: { start: 10, end: 20, colStart: 2, colEnd: 5 },
|
|
1417
1497
|
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1418
1498
|
const result = useVirtualScroll(props);
|
|
1419
|
-
expect(result.totalWidth.value).toBe(300); // (5-2) * 100
|
|
1420
|
-
expect(result.totalHeight.value).toBe(500); // (20-10) * 50
|
|
1499
|
+
expect(result.totalWidth.value).toBe(300); // (5-2) * 100 - gap(0)
|
|
1500
|
+
expect(result.totalHeight.value).toBe(500); // (20-10) * 50 - gap(0)
|
|
1421
1501
|
});
|
|
1422
1502
|
|
|
1423
1503
|
it('should handle SSR range with horizontal direction for total sizes', () => {
|
|
@@ -1428,7 +1508,7 @@ describe('useVirtualScroll', () => {
|
|
|
1428
1508
|
ssrRange: { start: 10, end: 20 },
|
|
1429
1509
|
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1430
1510
|
const result = useVirtualScroll(props);
|
|
1431
|
-
expect(result.totalWidth.value).toBe(500); // (20-10) * 50
|
|
1511
|
+
expect(result.totalWidth.value).toBe(500); // (20-10) * 50 - gap(0)
|
|
1432
1512
|
});
|
|
1433
1513
|
|
|
1434
1514
|
it('should handle SSR range with vertical offset in renderedItems', () => {
|
|
@@ -1450,8 +1530,8 @@ describe('useVirtualScroll', () => {
|
|
|
1450
1530
|
ssrRange: { start: 10, end: 20 },
|
|
1451
1531
|
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1452
1532
|
const result = useVirtualScroll(props);
|
|
1453
|
-
// ssrOffsetX = itemSizesX.query(10) = 10 *
|
|
1454
|
-
expect(result.renderedItems.value[ 0 ]?.offset.x).toBe(
|
|
1533
|
+
// ssrOffsetX = itemSizesX.query(10) = 10 * 40 = 400
|
|
1534
|
+
expect(result.renderedItems.value[ 0 ]?.offset.x).toBe(400);
|
|
1455
1535
|
});
|
|
1456
1536
|
|
|
1457
1537
|
it('should handle SSR range with dynamic sizes for total sizes', () => {
|
|
@@ -1462,7 +1542,7 @@ describe('useVirtualScroll', () => {
|
|
|
1462
1542
|
ssrRange: { start: 10, end: 20 },
|
|
1463
1543
|
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1464
1544
|
const result = useVirtualScroll(props);
|
|
1465
|
-
expect(result.totalHeight.value).toBe(
|
|
1545
|
+
expect(result.totalHeight.value).toBe(400); // (20-10) * 40 - gap(0)
|
|
1466
1546
|
});
|
|
1467
1547
|
|
|
1468
1548
|
it('should handle SSR range with dynamic horizontal sizes for total sizes', () => {
|
|
@@ -1473,7 +1553,7 @@ describe('useVirtualScroll', () => {
|
|
|
1473
1553
|
ssrRange: { start: 10, end: 20 },
|
|
1474
1554
|
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1475
1555
|
const result = useVirtualScroll(props);
|
|
1476
|
-
expect(result.totalWidth.value).toBe(
|
|
1556
|
+
expect(result.totalWidth.value).toBe(400); // (20-10) * 40 - 0 gap
|
|
1477
1557
|
});
|
|
1478
1558
|
|
|
1479
1559
|
it('should handle SSR range with both directions and dynamic offsets', () => {
|
|
@@ -1485,8 +1565,8 @@ describe('useVirtualScroll', () => {
|
|
|
1485
1565
|
ssrRange: { start: 10, end: 20, colStart: 2, colEnd: 5 },
|
|
1486
1566
|
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1487
1567
|
const result = useVirtualScroll(props);
|
|
1488
|
-
expect(result.
|
|
1489
|
-
expect(result.
|
|
1568
|
+
expect(result.totalWidth.value).toBe(300); // (5-2) * 100
|
|
1569
|
+
expect(result.totalHeight.value).toBe(400); // (20-10) * 40
|
|
1490
1570
|
});
|
|
1491
1571
|
|
|
1492
1572
|
it('should scroll to ssrRange on mount', async () => {
|
|
@@ -1547,9 +1627,362 @@ describe('useVirtualScroll', () => {
|
|
|
1547
1627
|
});
|
|
1548
1628
|
|
|
1549
1629
|
describe('helpers', () => {
|
|
1550
|
-
it('should
|
|
1630
|
+
it('should handle zero column count in totalWidth', async () => {
|
|
1631
|
+
const { result } = setup({ ...defaultProps, direction: 'both', columnCount: 0 });
|
|
1632
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1633
|
+
});
|
|
1634
|
+
|
|
1635
|
+
it('should handle vertical direction in totalWidth', () => {
|
|
1636
|
+
const { result } = setup({ ...defaultProps, direction: 'vertical' });
|
|
1637
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
it('should handle horizontal direction in totalHeight', () => {
|
|
1641
|
+
const { result } = setup({ ...defaultProps, direction: 'horizontal' });
|
|
1642
|
+
expect(result.totalHeight.value).toBe(0);
|
|
1643
|
+
});
|
|
1644
|
+
|
|
1645
|
+
it('should handle zero items in totalWidth/totalHeight', async () => {
|
|
1646
|
+
const { result } = setup({ ...defaultProps, items: [] });
|
|
1647
|
+
expect(result.totalHeight.value).toBe(0);
|
|
1648
|
+
|
|
1649
|
+
const { result: rH } = setup({ ...defaultProps, direction: 'horizontal', items: [] });
|
|
1650
|
+
expect(rH.totalWidth.value).toBe(0);
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
it('should cover SSR with zero items/columns', () => {
|
|
1654
|
+
const props = ref({
|
|
1655
|
+
items: [],
|
|
1656
|
+
direction: 'both',
|
|
1657
|
+
columnCount: 0,
|
|
1658
|
+
ssrRange: { start: 0, end: 0, colStart: 0, colEnd: 0 },
|
|
1659
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1660
|
+
const result = useVirtualScroll(props);
|
|
1661
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1662
|
+
expect(result.totalHeight.value).toBe(0);
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
it('should handle SSR range with both directions and no columns', () => {
|
|
1666
|
+
const props = ref({
|
|
1667
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1668
|
+
direction: 'both',
|
|
1669
|
+
columnCount: 0,
|
|
1670
|
+
ssrRange: { start: 10, end: 20, colStart: 0, colEnd: 0 },
|
|
1671
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1672
|
+
const result = useVirtualScroll(props);
|
|
1673
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
it('should handle SSR range with direction both and colCount > 0 but colEnd <= colStart', () => {
|
|
1677
|
+
const props = ref({
|
|
1678
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1679
|
+
direction: 'both',
|
|
1680
|
+
columnCount: 10,
|
|
1681
|
+
ssrRange: { start: 0, end: 10, colStart: 5, colEnd: 5 },
|
|
1682
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1683
|
+
const result = useVirtualScroll(props);
|
|
1684
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1685
|
+
});
|
|
1686
|
+
|
|
1687
|
+
it('should handle SSR range with vertical/both and end <= start', () => {
|
|
1688
|
+
const props = ref({
|
|
1689
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1690
|
+
direction: 'vertical',
|
|
1691
|
+
ssrRange: { start: 10, end: 10 },
|
|
1692
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1693
|
+
const result = useVirtualScroll(props);
|
|
1694
|
+
expect(result.totalHeight.value).toBe(0);
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
it('should handle SSR range with dynamic horizontal sizes for total sizes', () => {
|
|
1698
|
+
const props = ref({
|
|
1699
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1700
|
+
direction: 'horizontal',
|
|
1701
|
+
itemSize: 0,
|
|
1702
|
+
ssrRange: { start: 10, end: 20 },
|
|
1703
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1704
|
+
const result = useVirtualScroll(props);
|
|
1705
|
+
expect(result.totalWidth.value).toBe(400); // (20-10) * 40
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
it('should handle SSR range with both directions and dynamic offsets for total width', () => {
|
|
1709
|
+
const props = ref({
|
|
1710
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1711
|
+
direction: 'both',
|
|
1712
|
+
columnCount: 10,
|
|
1713
|
+
itemSize: 0,
|
|
1714
|
+
ssrRange: { start: 10, end: 20, colStart: 2, colEnd: 5 },
|
|
1715
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1716
|
+
const result = useVirtualScroll(props);
|
|
1717
|
+
expect(result.totalWidth.value).toBe(300);
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
it('should handle updateItemSizes with index < 0', async () => {
|
|
1721
|
+
const { result } = setup({ ...defaultProps, itemSize: undefined });
|
|
1722
|
+
result.updateItemSizes([ { index: -1, inlineSize: 100, blockSize: 100 } ]);
|
|
1723
|
+
await nextTick();
|
|
1724
|
+
// Should not change total height
|
|
1725
|
+
expect(result.totalHeight.value).toBe(4000);
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
it('should handle updateItemSizes with direction vertical and dynamic itemSize for X', async () => {
|
|
1729
|
+
const { result } = setup({ ...defaultProps, direction: 'vertical', itemSize: undefined });
|
|
1730
|
+
// Measured Items X should not be updated if direction is vertical
|
|
1731
|
+
result.updateItemSizes([ { index: 0, inlineSize: 100, blockSize: 100 } ]);
|
|
1732
|
+
await nextTick();
|
|
1733
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1734
|
+
});
|
|
1735
|
+
|
|
1736
|
+
it('should handle SSR with horizontal direction and fixedItemSize', () => {
|
|
1737
|
+
const propsValue = ref({
|
|
1738
|
+
direction: 'horizontal' as const,
|
|
1739
|
+
itemSize: 50,
|
|
1740
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1741
|
+
ssrRange: { end: 20, start: 10 },
|
|
1742
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1743
|
+
const result = useVirtualScroll(propsValue);
|
|
1744
|
+
expect(result.totalWidth.value).toBe(500); // (20-10) * 50 - 0 gap
|
|
1745
|
+
});
|
|
1746
|
+
|
|
1747
|
+
it('should handle SSR with vertical direction and fixedItemSize', () => {
|
|
1748
|
+
const propsValue = ref({
|
|
1749
|
+
direction: 'vertical' as const,
|
|
1750
|
+
itemSize: 50,
|
|
1751
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1752
|
+
ssrRange: { end: 20, start: 10 },
|
|
1753
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1754
|
+
const result = useVirtualScroll(propsValue);
|
|
1755
|
+
expect(result.totalHeight.value).toBe(500); // (20-10) * 50 - 0 gap
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
it('should handle SSR with direction both and fixedItemSize for totalHeight', () => {
|
|
1759
|
+
const propsValue = ref({
|
|
1760
|
+
direction: 'both' as const,
|
|
1761
|
+
columnCount: 10,
|
|
1762
|
+
itemSize: 50,
|
|
1763
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1764
|
+
ssrRange: { end: 20, start: 10 },
|
|
1765
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1766
|
+
const result = useVirtualScroll(propsValue);
|
|
1767
|
+
expect(result.totalHeight.value).toBe(500);
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
it('should handle SSR range with direction both and colEnd falsy', () => {
|
|
1771
|
+
const propsValue = ref({
|
|
1772
|
+
columnCount: 10,
|
|
1773
|
+
columnWidth: 100,
|
|
1774
|
+
direction: 'both' as const,
|
|
1775
|
+
items: Array.from({ length: 100 }, (_, i) => ({ id: i })),
|
|
1776
|
+
ssrRange: { colEnd: 0, colStart: 5, end: 10, start: 0 },
|
|
1777
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1778
|
+
const result = useVirtualScroll(propsValue);
|
|
1779
|
+
// colEnd is 0, so it should use colCount (10)
|
|
1780
|
+
// totalWidth = (10 - 5) * 100 = 500
|
|
1781
|
+
expect(result.totalWidth.value).toBe(500);
|
|
1782
|
+
});
|
|
1783
|
+
|
|
1784
|
+
it('should handle updateItemSizes with direction both and dynamic itemSize for Y', async () => {
|
|
1785
|
+
const { result } = setup({ ...defaultProps, direction: 'both', columnCount: 2, itemSize: undefined });
|
|
1786
|
+
// First measurement
|
|
1787
|
+
result.updateItemSizes([ { index: 0, inlineSize: 100, blockSize: 100 } ]);
|
|
1788
|
+
await nextTick();
|
|
1789
|
+
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(100);
|
|
1790
|
+
|
|
1791
|
+
// Increase
|
|
1792
|
+
result.updateItemSizes([ { index: 0, inlineSize: 100, blockSize: 120 } ]);
|
|
1793
|
+
await nextTick();
|
|
1794
|
+
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(120);
|
|
1795
|
+
|
|
1796
|
+
// Significant decrease
|
|
1797
|
+
result.updateItemSizes([ { index: 0, inlineSize: 100, blockSize: 100 } ]);
|
|
1798
|
+
await nextTick();
|
|
1799
|
+
expect(result.renderedItems.value[ 0 ]!.size.height).toBe(100);
|
|
1800
|
+
});
|
|
1801
|
+
|
|
1802
|
+
it('should handle object padding branches in helpers', () => {
|
|
1551
1803
|
expect(getPaddingX({ x: 10 }, 'horizontal')).toBe(10);
|
|
1552
1804
|
expect(getPaddingY({ y: 20 }, 'vertical')).toBe(20);
|
|
1553
1805
|
});
|
|
1806
|
+
|
|
1807
|
+
it('should cover totalWidth SSR len <= 0', () => {
|
|
1808
|
+
const propsValue = ref({
|
|
1809
|
+
items: mockItems,
|
|
1810
|
+
direction: 'horizontal' as const,
|
|
1811
|
+
itemSize: 50,
|
|
1812
|
+
ssrRange: { start: 10, end: 10 },
|
|
1813
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1814
|
+
const result = useVirtualScroll(propsValue);
|
|
1815
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1816
|
+
});
|
|
1817
|
+
|
|
1818
|
+
it('should cover totalWidth SSR end <= start for dynamic sizes', () => {
|
|
1819
|
+
const propsValue = ref({
|
|
1820
|
+
items: mockItems,
|
|
1821
|
+
direction: 'horizontal' as const,
|
|
1822
|
+
itemSize: undefined,
|
|
1823
|
+
ssrRange: { start: 10, end: 10 },
|
|
1824
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1825
|
+
const result = useVirtualScroll(propsValue);
|
|
1826
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1827
|
+
});
|
|
1828
|
+
|
|
1829
|
+
it('should cover totalWidth non-SSR items.length <= 0 for dynamic sizes', () => {
|
|
1830
|
+
const propsValue = ref({
|
|
1831
|
+
items: [],
|
|
1832
|
+
direction: 'horizontal' as const,
|
|
1833
|
+
itemSize: undefined,
|
|
1834
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1835
|
+
const result = useVirtualScroll(propsValue);
|
|
1836
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1837
|
+
});
|
|
1838
|
+
|
|
1839
|
+
it('should cover totalHeight SSR len <= 0', () => {
|
|
1840
|
+
const propsValue = ref({
|
|
1841
|
+
items: mockItems,
|
|
1842
|
+
direction: 'vertical' as const,
|
|
1843
|
+
itemSize: 50,
|
|
1844
|
+
ssrRange: { start: 10, end: 10 },
|
|
1845
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1846
|
+
const result = useVirtualScroll(propsValue);
|
|
1847
|
+
expect(result.totalHeight.value).toBe(0);
|
|
1848
|
+
});
|
|
1849
|
+
|
|
1850
|
+
it('should cover totalHeight SSR end <= start for dynamic sizes', () => {
|
|
1851
|
+
const propsValue = ref({
|
|
1852
|
+
items: mockItems,
|
|
1853
|
+
direction: 'vertical' as const,
|
|
1854
|
+
itemSize: undefined,
|
|
1855
|
+
ssrRange: { start: 10, end: 10 },
|
|
1856
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1857
|
+
const result = useVirtualScroll(propsValue);
|
|
1858
|
+
expect(result.totalHeight.value).toBe(0);
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
it('should cover totalHeight non-SSR items.length <= 0 for dynamic sizes', () => {
|
|
1862
|
+
const propsValue = ref({
|
|
1863
|
+
items: [],
|
|
1864
|
+
direction: 'vertical' as const,
|
|
1865
|
+
itemSize: undefined,
|
|
1866
|
+
}) as Ref<VirtualScrollProps<unknown>>;
|
|
1867
|
+
const result = useVirtualScroll(propsValue);
|
|
1868
|
+
expect(result.totalHeight.value).toBe(0);
|
|
1869
|
+
});
|
|
1870
|
+
|
|
1871
|
+
describe('internal sizing stabilization and edge cases', () => {
|
|
1872
|
+
const mockItems = Array.from({ length: 10 }, (_, i) => ({ id: i }));
|
|
1873
|
+
|
|
1874
|
+
it('should skip re-initializing sizes for already measured dynamic items', async () => {
|
|
1875
|
+
const props = ref({
|
|
1876
|
+
items: mockItems,
|
|
1877
|
+
direction: 'both' as const,
|
|
1878
|
+
columnCount: 2,
|
|
1879
|
+
}) as Ref<VirtualScrollProps<{ id: number; }>>;
|
|
1880
|
+
|
|
1881
|
+
const result = useVirtualScroll(props);
|
|
1882
|
+
await nextTick();
|
|
1883
|
+
|
|
1884
|
+
// Measure item 0 and col 0
|
|
1885
|
+
const parent = document.createElement('div');
|
|
1886
|
+
const col0 = document.createElement('div');
|
|
1887
|
+
col0.dataset.colIndex = '0';
|
|
1888
|
+
Object.defineProperty(col0, 'offsetWidth', { value: 200 });
|
|
1889
|
+
parent.appendChild(col0);
|
|
1890
|
+
|
|
1891
|
+
result.updateItemSizes([ { index: 0, inlineSize: 200, blockSize: 150, element: parent } ]);
|
|
1892
|
+
await nextTick();
|
|
1893
|
+
|
|
1894
|
+
expect(result.getColumnWidth(0)).toBe(200);
|
|
1895
|
+
expect(result.renderedItems.value[ 0 ]?.size.height).toBe(150);
|
|
1896
|
+
|
|
1897
|
+
// Trigger initializeSizes by changing items length
|
|
1898
|
+
props.value.items = Array.from({ length: 11 }, (_, i) => ({ id: i }));
|
|
1899
|
+
await nextTick();
|
|
1900
|
+
|
|
1901
|
+
// Should NOT reset already measured item 0
|
|
1902
|
+
expect(result.getColumnWidth(0)).toBe(200);
|
|
1903
|
+
expect(result.renderedItems.value[ 0 ]?.size.height).toBe(150);
|
|
1904
|
+
});
|
|
1905
|
+
|
|
1906
|
+
it('should mark items as measured when fixed size matches current size within tolerance', async () => {
|
|
1907
|
+
const props = ref({
|
|
1908
|
+
items: mockItems,
|
|
1909
|
+
direction: 'horizontal' as const,
|
|
1910
|
+
itemSize: 50,
|
|
1911
|
+
}) as Ref<VirtualScrollProps<{ id: number; }>>;
|
|
1912
|
+
|
|
1913
|
+
useVirtualScroll(props);
|
|
1914
|
+
await nextTick();
|
|
1915
|
+
|
|
1916
|
+
// Trigger initializeSizes again with same prop
|
|
1917
|
+
props.value.columnGap = 0;
|
|
1918
|
+
await nextTick();
|
|
1919
|
+
// Hits the branch where Math.abs(current - target) <= 0.5
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1922
|
+
it('should mark columns as measured when fixed width matches current width within tolerance', async () => {
|
|
1923
|
+
const props = ref({
|
|
1924
|
+
items: mockItems,
|
|
1925
|
+
direction: 'both' as const,
|
|
1926
|
+
columnCount: 2,
|
|
1927
|
+
columnWidth: 100,
|
|
1928
|
+
}) as Ref<VirtualScrollProps<{ id: number; }>>;
|
|
1929
|
+
|
|
1930
|
+
useVirtualScroll(props);
|
|
1931
|
+
await nextTick();
|
|
1932
|
+
|
|
1933
|
+
props.value.columnGap = 0;
|
|
1934
|
+
await nextTick();
|
|
1935
|
+
});
|
|
1936
|
+
|
|
1937
|
+
it('should reset item sizes when switching between horizontal and vertical directions', async () => {
|
|
1938
|
+
const props = ref({
|
|
1939
|
+
items: mockItems,
|
|
1940
|
+
direction: 'horizontal' as const,
|
|
1941
|
+
itemSize: 50,
|
|
1942
|
+
}) as Ref<VirtualScrollProps<{ id: number; }>>;
|
|
1943
|
+
|
|
1944
|
+
const result = useVirtualScroll(props);
|
|
1945
|
+
await nextTick();
|
|
1946
|
+
expect(result.totalWidth.value).toBe(500);
|
|
1947
|
+
|
|
1948
|
+
// Switch to vertical (resets X)
|
|
1949
|
+
props.value.direction = 'vertical';
|
|
1950
|
+
await nextTick();
|
|
1951
|
+
expect(result.totalWidth.value).toBe(0);
|
|
1952
|
+
|
|
1953
|
+
// Switch to both
|
|
1954
|
+
props.value.direction = 'both';
|
|
1955
|
+
props.value.columnCount = 10;
|
|
1956
|
+
props.value.columnWidth = 100;
|
|
1957
|
+
await nextTick();
|
|
1958
|
+
expect(result.totalHeight.value).toBe(500);
|
|
1959
|
+
expect(result.totalWidth.value).toBe(1000);
|
|
1960
|
+
|
|
1961
|
+
// Switch to horizontal (resets Y)
|
|
1962
|
+
props.value.direction = 'horizontal';
|
|
1963
|
+
await nextTick();
|
|
1964
|
+
await nextTick();
|
|
1965
|
+
expect(result.totalHeight.value).toBe(0);
|
|
1966
|
+
});
|
|
1967
|
+
|
|
1968
|
+
it('should skip re-initialization if dynamic size is already measured and non-zero', async () => {
|
|
1969
|
+
const props = ref({
|
|
1970
|
+
items: mockItems,
|
|
1971
|
+
direction: 'horizontal' as const,
|
|
1972
|
+
itemSize: undefined, // dynamic
|
|
1973
|
+
}) as Ref<VirtualScrollProps<{ id: number; }>>;
|
|
1974
|
+
|
|
1975
|
+
const result = useVirtualScroll(props);
|
|
1976
|
+
await nextTick();
|
|
1977
|
+
|
|
1978
|
+
result.updateItemSizes([ { index: 0, inlineSize: 100, blockSize: 50, element: document.createElement('div') } ]);
|
|
1979
|
+
await nextTick();
|
|
1980
|
+
|
|
1981
|
+
props.value.gap = 1;
|
|
1982
|
+
await nextTick();
|
|
1983
|
+
|
|
1984
|
+
expect(result.totalWidth.value).toBeGreaterThan(0);
|
|
1985
|
+
});
|
|
1986
|
+
});
|
|
1554
1987
|
});
|
|
1555
1988
|
});
|