@pdanpdan/virtual-scroll 0.4.0 → 0.6.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 +172 -324
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +836 -376
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1334 -741
- package/dist/index.mjs.map +1 -1
- package/dist/virtual-scroll.css +1 -1
- package/package.json +8 -2
- package/src/components/VirtualScroll.test.ts +1921 -325
- package/src/components/VirtualScroll.vue +829 -386
- package/src/components/VirtualScrollbar.test.ts +174 -0
- package/src/components/VirtualScrollbar.vue +102 -0
- package/src/composables/useVirtualScroll.test.ts +1506 -228
- package/src/composables/useVirtualScroll.ts +869 -517
- package/src/composables/useVirtualScrollbar.test.ts +526 -0
- package/src/composables/useVirtualScrollbar.ts +244 -0
- package/src/index.ts +9 -0
- package/src/types.ts +353 -110
- package/src/utils/fenwick-tree.test.ts +39 -39
- package/src/utils/scroll.test.ts +181 -101
- package/src/utils/scroll.ts +43 -5
- package/src/utils/virtual-scroll-logic.test.ts +673 -323
- package/src/utils/virtual-scroll-logic.ts +759 -430
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from './virtual-scroll-logic';
|
|
14
14
|
|
|
15
15
|
describe('virtual-scroll-logic', () => {
|
|
16
|
-
describe('
|
|
16
|
+
describe('calculate total size', () => {
|
|
17
17
|
it('calculates vertical total size with fixed size', () => {
|
|
18
18
|
const result = calculateTotalSize({
|
|
19
19
|
columnCount: 0,
|
|
@@ -29,10 +29,31 @@ describe('virtual-scroll-logic', () => {
|
|
|
29
29
|
usableHeight: 500,
|
|
30
30
|
usableWidth: 500,
|
|
31
31
|
});
|
|
32
|
+
// 50 * 100 + 10 * 99
|
|
32
33
|
expect(result.height).toBe(5990);
|
|
33
34
|
expect(result.width).toBe(500);
|
|
34
35
|
});
|
|
35
36
|
|
|
37
|
+
it('calculates vertical total size with dynamic sizes', () => {
|
|
38
|
+
const result = calculateTotalSize({
|
|
39
|
+
columnCount: 0,
|
|
40
|
+
columnGap: 0,
|
|
41
|
+
direction: 'vertical',
|
|
42
|
+
fixedSize: null,
|
|
43
|
+
fixedWidth: null,
|
|
44
|
+
gap: 5,
|
|
45
|
+
itemsLength: 10,
|
|
46
|
+
queryColumn: () => 0,
|
|
47
|
+
queryX: () => 0,
|
|
48
|
+
queryY: (idx) => idx * 45,
|
|
49
|
+
usableHeight: 500,
|
|
50
|
+
usableWidth: 500,
|
|
51
|
+
});
|
|
52
|
+
// 45 * 10 - 5 (gap after last item)
|
|
53
|
+
expect(result.height).toBe(445);
|
|
54
|
+
expect(result.width).toBe(500);
|
|
55
|
+
});
|
|
56
|
+
|
|
36
57
|
it('calculates horizontal total size with fixed size', () => {
|
|
37
58
|
const result = calculateTotalSize({
|
|
38
59
|
columnCount: 0,
|
|
@@ -52,23 +73,23 @@ describe('virtual-scroll-logic', () => {
|
|
|
52
73
|
expect(result.height).toBe(500);
|
|
53
74
|
});
|
|
54
75
|
|
|
55
|
-
it('calculates
|
|
76
|
+
it('calculates horizontal total size with dynamic sizes', () => {
|
|
56
77
|
const result = calculateTotalSize({
|
|
57
|
-
columnCount:
|
|
58
|
-
columnGap:
|
|
59
|
-
direction: '
|
|
60
|
-
fixedSize:
|
|
78
|
+
columnCount: 0,
|
|
79
|
+
columnGap: 10,
|
|
80
|
+
direction: 'horizontal',
|
|
81
|
+
fixedSize: null,
|
|
61
82
|
fixedWidth: null,
|
|
62
|
-
gap:
|
|
83
|
+
gap: 0,
|
|
63
84
|
itemsLength: 100,
|
|
64
|
-
queryColumn: (
|
|
65
|
-
queryX: () =>
|
|
85
|
+
queryColumn: () => 0,
|
|
86
|
+
queryX: (idx) => idx * 60,
|
|
66
87
|
queryY: () => 0,
|
|
67
88
|
usableHeight: 500,
|
|
68
89
|
usableWidth: 500,
|
|
69
90
|
});
|
|
70
|
-
expect(result.
|
|
71
|
-
expect(result.
|
|
91
|
+
expect(result.width).toBe(5990);
|
|
92
|
+
expect(result.height).toBe(500);
|
|
72
93
|
});
|
|
73
94
|
|
|
74
95
|
it('calculates grid (both) total size with fixed sizes', () => {
|
|
@@ -90,18 +111,18 @@ describe('virtual-scroll-logic', () => {
|
|
|
90
111
|
expect(result.width).toBe(520);
|
|
91
112
|
});
|
|
92
113
|
|
|
93
|
-
it('calculates grid (both) total size with dynamic
|
|
114
|
+
it('calculates grid (both) total size with fixed row size and dynamic column width', () => {
|
|
94
115
|
const result = calculateTotalSize({
|
|
95
116
|
columnCount: 5,
|
|
96
117
|
columnGap: 5,
|
|
97
118
|
direction: 'both',
|
|
98
|
-
fixedSize:
|
|
119
|
+
fixedSize: 50,
|
|
99
120
|
fixedWidth: null,
|
|
100
121
|
gap: 10,
|
|
101
122
|
itemsLength: 100,
|
|
102
123
|
queryColumn: (idx) => idx * 105,
|
|
103
124
|
queryX: () => 0,
|
|
104
|
-
queryY: (
|
|
125
|
+
queryY: () => 0,
|
|
105
126
|
usableHeight: 500,
|
|
106
127
|
usableWidth: 500,
|
|
107
128
|
});
|
|
@@ -109,40 +130,23 @@ describe('virtual-scroll-logic', () => {
|
|
|
109
130
|
expect(result.width).toBe(520);
|
|
110
131
|
});
|
|
111
132
|
|
|
112
|
-
it('calculates
|
|
133
|
+
it('calculates grid (both) total size with dynamic sizes', () => {
|
|
113
134
|
const result = calculateTotalSize({
|
|
114
|
-
columnCount:
|
|
115
|
-
columnGap:
|
|
116
|
-
direction: '
|
|
135
|
+
columnCount: 5,
|
|
136
|
+
columnGap: 5,
|
|
137
|
+
direction: 'both',
|
|
117
138
|
fixedSize: null,
|
|
118
139
|
fixedWidth: null,
|
|
119
|
-
gap:
|
|
140
|
+
gap: 10,
|
|
120
141
|
itemsLength: 100,
|
|
121
|
-
queryColumn: () =>
|
|
122
|
-
queryX: (idx) => idx * 60,
|
|
123
|
-
queryY: () => 0,
|
|
124
|
-
usableHeight: 500,
|
|
125
|
-
usableWidth: 500,
|
|
126
|
-
});
|
|
127
|
-
expect(result.width).toBe(5990);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('calculates vertical total size with dynamic sizes', () => {
|
|
131
|
-
const result = calculateTotalSize({
|
|
132
|
-
columnCount: 0,
|
|
133
|
-
columnGap: 0,
|
|
134
|
-
direction: 'vertical',
|
|
135
|
-
fixedSize: null,
|
|
136
|
-
fixedWidth: null,
|
|
137
|
-
gap: 5,
|
|
138
|
-
itemsLength: 10,
|
|
139
|
-
queryColumn: () => 0,
|
|
142
|
+
queryColumn: (idx) => idx * 105,
|
|
140
143
|
queryX: () => 0,
|
|
141
|
-
queryY: (idx) => idx *
|
|
144
|
+
queryY: (idx) => idx * 60,
|
|
142
145
|
usableHeight: 500,
|
|
143
146
|
usableWidth: 500,
|
|
144
147
|
});
|
|
145
|
-
expect(result.height).toBe(
|
|
148
|
+
expect(result.height).toBe(5990);
|
|
149
|
+
expect(result.width).toBe(520);
|
|
146
150
|
});
|
|
147
151
|
|
|
148
152
|
it('calculates total sizes for single item (both, fixed rows, fixed cols)', () => {
|
|
@@ -160,8 +164,6 @@ describe('virtual-scroll-logic', () => {
|
|
|
160
164
|
usableHeight: 500,
|
|
161
165
|
usableWidth: 500,
|
|
162
166
|
});
|
|
163
|
-
// columnCount=1 * (100 + 10) - 10 = 100.
|
|
164
|
-
// itemsLength=1 * (50 + 10) - 10 = 50.
|
|
165
167
|
expect(result.height).toBe(500);
|
|
166
168
|
expect(result.width).toBe(500);
|
|
167
169
|
});
|
|
@@ -199,7 +201,6 @@ describe('virtual-scroll-logic', () => {
|
|
|
199
201
|
usableHeight: 500,
|
|
200
202
|
usableWidth: 500,
|
|
201
203
|
});
|
|
202
|
-
// queryX(1) = 60. gap = 10. total = 60 - 10 = 50.
|
|
203
204
|
expect(result.width).toBe(50);
|
|
204
205
|
});
|
|
205
206
|
|
|
@@ -218,11 +219,10 @@ describe('virtual-scroll-logic', () => {
|
|
|
218
219
|
usableHeight: 500,
|
|
219
220
|
usableWidth: 500,
|
|
220
221
|
});
|
|
221
|
-
// queryY(1) = 60. gap = 10. total = 60 - 10 = 50.
|
|
222
222
|
expect(result.height).toBe(50);
|
|
223
223
|
});
|
|
224
224
|
|
|
225
|
-
it('calculates total height for single small item (vertical, dynamic size,
|
|
225
|
+
it('calculates total height for single small item (vertical, dynamic size, itemslength 1)', () => {
|
|
226
226
|
const result = calculateTotalSize({
|
|
227
227
|
columnCount: 0,
|
|
228
228
|
columnGap: 0,
|
|
@@ -240,7 +240,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
240
240
|
expect(result.height).toBe(0);
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
-
it('calculates total width for single small item (horizontal, dynamic size,
|
|
243
|
+
it('calculates total width for single small item (horizontal, dynamic size, itemslength 1)', () => {
|
|
244
244
|
const result = calculateTotalSize({
|
|
245
245
|
columnCount: 0,
|
|
246
246
|
columnGap: 10,
|
|
@@ -258,7 +258,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
258
258
|
expect(result.width).toBe(0);
|
|
259
259
|
});
|
|
260
260
|
|
|
261
|
-
it('calculates total height for single item (both, dynamic size,
|
|
261
|
+
it('calculates total height for single item (both, dynamic size, queryy)', () => {
|
|
262
262
|
const result = calculateTotalSize({
|
|
263
263
|
columnCount: 1,
|
|
264
264
|
columnGap: 10,
|
|
@@ -315,14 +315,14 @@ describe('virtual-scroll-logic', () => {
|
|
|
315
315
|
expect(result.width).toBe(500);
|
|
316
316
|
});
|
|
317
317
|
|
|
318
|
-
it('returns
|
|
318
|
+
it('returns viewport size for empty items with dynamic sizes (both)', () => {
|
|
319
319
|
const result = calculateTotalSize({
|
|
320
320
|
columnCount: 0,
|
|
321
321
|
columnGap: 10,
|
|
322
|
-
direction: '
|
|
323
|
-
fixedSize:
|
|
322
|
+
direction: 'both',
|
|
323
|
+
fixedSize: null,
|
|
324
324
|
fixedWidth: null,
|
|
325
|
-
gap:
|
|
325
|
+
gap: 10,
|
|
326
326
|
itemsLength: 0,
|
|
327
327
|
queryColumn: () => 0,
|
|
328
328
|
queryX: () => 0,
|
|
@@ -330,17 +330,18 @@ describe('virtual-scroll-logic', () => {
|
|
|
330
330
|
usableHeight: 500,
|
|
331
331
|
usableWidth: 500,
|
|
332
332
|
});
|
|
333
|
-
expect(result.
|
|
333
|
+
expect(result.height).toBe(500);
|
|
334
|
+
expect(result.width).toBe(500);
|
|
334
335
|
});
|
|
335
336
|
|
|
336
|
-
it('returns 0 for empty items with fixed sizes (
|
|
337
|
+
it('returns 0 for empty items with fixed sizes (horizontal)', () => {
|
|
337
338
|
const result = calculateTotalSize({
|
|
338
339
|
columnCount: 0,
|
|
339
|
-
columnGap:
|
|
340
|
-
direction: '
|
|
340
|
+
columnGap: 10,
|
|
341
|
+
direction: 'horizontal',
|
|
341
342
|
fixedSize: 50,
|
|
342
343
|
fixedWidth: null,
|
|
343
|
-
gap:
|
|
344
|
+
gap: 0,
|
|
344
345
|
itemsLength: 0,
|
|
345
346
|
queryColumn: () => 0,
|
|
346
347
|
queryX: () => 0,
|
|
@@ -348,15 +349,15 @@ describe('virtual-scroll-logic', () => {
|
|
|
348
349
|
usableHeight: 500,
|
|
349
350
|
usableWidth: 500,
|
|
350
351
|
});
|
|
351
|
-
expect(result.
|
|
352
|
+
expect(result.width).toBe(0);
|
|
352
353
|
});
|
|
353
354
|
|
|
354
|
-
it('returns
|
|
355
|
+
it('returns 0 for empty items with fixed sizes (vertical)', () => {
|
|
355
356
|
const result = calculateTotalSize({
|
|
356
357
|
columnCount: 0,
|
|
357
|
-
columnGap:
|
|
358
|
-
direction: '
|
|
359
|
-
fixedSize:
|
|
358
|
+
columnGap: 0,
|
|
359
|
+
direction: 'vertical',
|
|
360
|
+
fixedSize: 50,
|
|
360
361
|
fixedWidth: null,
|
|
361
362
|
gap: 10,
|
|
362
363
|
itemsLength: 0,
|
|
@@ -366,8 +367,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
366
367
|
usableHeight: 500,
|
|
367
368
|
usableWidth: 500,
|
|
368
369
|
});
|
|
369
|
-
expect(result.height).toBe(
|
|
370
|
-
expect(result.width).toBe(500);
|
|
370
|
+
expect(result.height).toBe(0);
|
|
371
371
|
});
|
|
372
372
|
|
|
373
373
|
it('returns 0 for empty items with dynamic sizes (horizontal)', () => {
|
|
@@ -407,7 +407,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
407
407
|
});
|
|
408
408
|
});
|
|
409
409
|
|
|
410
|
-
describe('
|
|
410
|
+
describe('calculate range', () => {
|
|
411
411
|
it('calculates vertical range with dynamic size', () => {
|
|
412
412
|
const result = calculateRange({
|
|
413
413
|
bufferAfter: 0,
|
|
@@ -448,11 +448,6 @@ describe('virtual-scroll-logic', () => {
|
|
|
448
448
|
usableHeight: 500,
|
|
449
449
|
usableWidth: 100,
|
|
450
450
|
});
|
|
451
|
-
// item 0: 0-50, gap 50-60
|
|
452
|
-
// item 1: 60-110, gap 110-120
|
|
453
|
-
// item 2: 120-170, gap 170-180
|
|
454
|
-
// scroll 120 -> item 2.
|
|
455
|
-
// viewport 100 -> ends at 220. item 3: 180-230.
|
|
456
451
|
expect(result.start).toBe(2);
|
|
457
452
|
expect(result.end).toBe(4);
|
|
458
453
|
});
|
|
@@ -502,14 +497,6 @@ describe('virtual-scroll-logic', () => {
|
|
|
502
497
|
});
|
|
503
498
|
|
|
504
499
|
it('calculates horizontal range with dynamic size where end item is partially visible (edge case)', () => {
|
|
505
|
-
// Setup:
|
|
506
|
-
// Item 0: 0-100
|
|
507
|
-
// Item 1: 100-200
|
|
508
|
-
// Viewport: 0-150.
|
|
509
|
-
// Target end = 150.
|
|
510
|
-
// findLowerBoundX(150) -> returns 1 because queryX(1)=100 <= 150, queryX(2)=200 > 150.
|
|
511
|
-
// queryX(1) = 100 < 150.
|
|
512
|
-
// So end should increment to 2.
|
|
513
500
|
const result = calculateRange({
|
|
514
501
|
bufferAfter: 0,
|
|
515
502
|
bufferBefore: 0,
|
|
@@ -532,11 +519,16 @@ describe('virtual-scroll-logic', () => {
|
|
|
532
519
|
});
|
|
533
520
|
});
|
|
534
521
|
|
|
535
|
-
describe('
|
|
522
|
+
describe('calculate scroll target', () => {
|
|
536
523
|
it('calculates target for horizontal end alignment', () => {
|
|
537
524
|
const result = calculateScrollTarget({
|
|
525
|
+
scaleX: 1,
|
|
526
|
+
scaleY: 1,
|
|
527
|
+
hostOffsetX: 0,
|
|
528
|
+
hostOffsetY: 0,
|
|
538
529
|
colIndex: 10,
|
|
539
|
-
|
|
530
|
+
viewportWidth: 500,
|
|
531
|
+
viewportHeight: 500,
|
|
540
532
|
columnGap: 0,
|
|
541
533
|
direction: 'horizontal',
|
|
542
534
|
fixedSize: 50,
|
|
@@ -548,24 +540,25 @@ describe('virtual-scroll-logic', () => {
|
|
|
548
540
|
getItemQueryY: () => 0,
|
|
549
541
|
getItemSizeX: () => 50,
|
|
550
542
|
getItemSizeY: () => 0,
|
|
551
|
-
itemsLength: 100,
|
|
552
543
|
options: 'end',
|
|
553
544
|
relativeScrollX: 0,
|
|
554
545
|
relativeScrollY: 0,
|
|
555
546
|
rowIndex: null,
|
|
556
547
|
totalHeight: 0,
|
|
557
548
|
totalWidth: 5000,
|
|
558
|
-
usableHeight: 500,
|
|
559
|
-
usableWidth: 500,
|
|
560
549
|
});
|
|
561
|
-
// item 10 at 500. ends at 550. viewport 500 -> targetX = 550 - 500 = 50.
|
|
562
550
|
expect(result.targetX).toBe(50);
|
|
563
551
|
});
|
|
564
552
|
|
|
565
553
|
it('calculates target for grid column start alignment', () => {
|
|
566
554
|
const result = calculateScrollTarget({
|
|
555
|
+
scaleX: 1,
|
|
556
|
+
scaleY: 1,
|
|
557
|
+
hostOffsetX: 0,
|
|
558
|
+
hostOffsetY: 0,
|
|
567
559
|
colIndex: 10,
|
|
568
|
-
|
|
560
|
+
viewportWidth: 500,
|
|
561
|
+
viewportHeight: 500,
|
|
569
562
|
columnGap: 10,
|
|
570
563
|
direction: 'both',
|
|
571
564
|
fixedSize: null,
|
|
@@ -577,23 +570,25 @@ describe('virtual-scroll-logic', () => {
|
|
|
577
570
|
getItemQueryY: () => 0,
|
|
578
571
|
getItemSizeX: () => 0,
|
|
579
572
|
getItemSizeY: () => 0,
|
|
580
|
-
itemsLength: 100,
|
|
581
573
|
options: { align: { x: 'start' } },
|
|
582
574
|
relativeScrollX: 0,
|
|
583
575
|
relativeScrollY: 0,
|
|
584
576
|
rowIndex: null,
|
|
585
577
|
totalHeight: 0,
|
|
586
578
|
totalWidth: 5500,
|
|
587
|
-
usableHeight: 500,
|
|
588
|
-
usableWidth: 500,
|
|
589
579
|
});
|
|
590
580
|
expect(result.targetX).toBe(1100);
|
|
591
581
|
});
|
|
592
582
|
|
|
593
583
|
it('calculates target for vertical start alignment with partial align in options object', () => {
|
|
594
584
|
const result = calculateScrollTarget({
|
|
585
|
+
scaleX: 1,
|
|
586
|
+
scaleY: 1,
|
|
587
|
+
hostOffsetX: 0,
|
|
588
|
+
hostOffsetY: 0,
|
|
595
589
|
colIndex: null,
|
|
596
|
-
|
|
590
|
+
viewportWidth: 500,
|
|
591
|
+
viewportHeight: 500,
|
|
597
592
|
columnGap: 0,
|
|
598
593
|
direction: 'vertical',
|
|
599
594
|
fixedSize: 50,
|
|
@@ -605,24 +600,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
605
600
|
getItemQueryY: (idx) => idx * 50,
|
|
606
601
|
getItemSizeX: () => 0,
|
|
607
602
|
getItemSizeY: () => 50,
|
|
608
|
-
|
|
609
|
-
options: { align: { y: 'start' } }, // x is missing
|
|
603
|
+
options: { align: { y: 'start' } },
|
|
610
604
|
relativeScrollX: 50,
|
|
611
605
|
relativeScrollY: 0,
|
|
612
606
|
rowIndex: 10,
|
|
613
607
|
totalHeight: 5000,
|
|
614
608
|
totalWidth: 5000,
|
|
615
|
-
usableHeight: 500,
|
|
616
|
-
usableWidth: 500,
|
|
617
609
|
});
|
|
618
610
|
expect(result.targetY).toBe(500);
|
|
619
|
-
expect(result.targetX).toBe(50);
|
|
611
|
+
expect(result.targetX).toBe(50);
|
|
620
612
|
});
|
|
621
613
|
|
|
622
614
|
it('calculates target for horizontal start alignment with partial options object', () => {
|
|
623
615
|
const result = calculateScrollTarget({
|
|
616
|
+
scaleX: 1,
|
|
617
|
+
scaleY: 1,
|
|
618
|
+
hostOffsetX: 0,
|
|
619
|
+
hostOffsetY: 0,
|
|
624
620
|
colIndex: 10,
|
|
625
|
-
|
|
621
|
+
viewportWidth: 500,
|
|
622
|
+
viewportHeight: 500,
|
|
626
623
|
columnGap: 0,
|
|
627
624
|
direction: 'horizontal',
|
|
628
625
|
fixedSize: 50,
|
|
@@ -634,24 +631,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
634
631
|
getItemQueryY: () => 0,
|
|
635
632
|
getItemSizeX: () => 50,
|
|
636
633
|
getItemSizeY: () => 0,
|
|
637
|
-
|
|
638
|
-
options: { align: { x: 'start' } }, // y is missing, should default to 'auto'
|
|
634
|
+
options: { align: { x: 'start' } },
|
|
639
635
|
relativeScrollX: 0,
|
|
640
636
|
relativeScrollY: 50,
|
|
641
637
|
rowIndex: 10,
|
|
642
638
|
totalHeight: 5000,
|
|
643
639
|
totalWidth: 5000,
|
|
644
|
-
usableHeight: 500,
|
|
645
|
-
usableWidth: 500,
|
|
646
640
|
});
|
|
647
641
|
expect(result.targetX).toBe(500);
|
|
648
|
-
expect(result.targetY).toBe(50);
|
|
642
|
+
expect(result.targetY).toBe(50);
|
|
649
643
|
});
|
|
650
644
|
|
|
651
645
|
it('calculates target for horizontal start alignment with options object', () => {
|
|
652
646
|
const result = calculateScrollTarget({
|
|
647
|
+
scaleX: 1,
|
|
648
|
+
scaleY: 1,
|
|
649
|
+
hostOffsetX: 0,
|
|
650
|
+
hostOffsetY: 0,
|
|
653
651
|
colIndex: 10,
|
|
654
|
-
|
|
652
|
+
viewportWidth: 500,
|
|
653
|
+
viewportHeight: 500,
|
|
655
654
|
columnGap: 0,
|
|
656
655
|
direction: 'horizontal',
|
|
657
656
|
fixedSize: 50,
|
|
@@ -663,23 +662,25 @@ describe('virtual-scroll-logic', () => {
|
|
|
663
662
|
getItemQueryY: () => 0,
|
|
664
663
|
getItemSizeX: () => 50,
|
|
665
664
|
getItemSizeY: () => 0,
|
|
666
|
-
itemsLength: 100,
|
|
667
665
|
options: { align: { x: 'start' } },
|
|
668
666
|
relativeScrollX: 0,
|
|
669
667
|
relativeScrollY: 0,
|
|
670
668
|
rowIndex: null,
|
|
671
669
|
totalHeight: 0,
|
|
672
670
|
totalWidth: 5000,
|
|
673
|
-
usableHeight: 500,
|
|
674
|
-
usableWidth: 500,
|
|
675
671
|
});
|
|
676
672
|
expect(result.targetX).toBe(500);
|
|
677
673
|
});
|
|
678
674
|
|
|
679
675
|
it('calculates target for vertical start alignment with dynamic size', () => {
|
|
680
676
|
const result = calculateScrollTarget({
|
|
677
|
+
scaleX: 1,
|
|
678
|
+
scaleY: 1,
|
|
679
|
+
hostOffsetX: 0,
|
|
680
|
+
hostOffsetY: 0,
|
|
681
681
|
colIndex: null,
|
|
682
|
-
|
|
682
|
+
viewportWidth: 500,
|
|
683
|
+
viewportHeight: 500,
|
|
683
684
|
columnGap: 0,
|
|
684
685
|
direction: 'vertical',
|
|
685
686
|
fixedSize: null,
|
|
@@ -691,15 +692,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
691
692
|
getItemQueryY: (idx) => idx * 60,
|
|
692
693
|
getItemSizeX: () => 0,
|
|
693
694
|
getItemSizeY: () => 60,
|
|
694
|
-
itemsLength: 100,
|
|
695
695
|
options: 'start',
|
|
696
696
|
relativeScrollX: 0,
|
|
697
697
|
relativeScrollY: 0,
|
|
698
698
|
rowIndex: 10,
|
|
699
699
|
totalHeight: 6000,
|
|
700
700
|
totalWidth: 0,
|
|
701
|
-
usableHeight: 500,
|
|
702
|
-
usableWidth: 500,
|
|
703
701
|
});
|
|
704
702
|
expect(result.targetY).toBe(600);
|
|
705
703
|
expect(result.itemHeight).toBe(50);
|
|
@@ -707,8 +705,13 @@ describe('virtual-scroll-logic', () => {
|
|
|
707
705
|
|
|
708
706
|
it('calculates target for horizontal start alignment with dynamic size', () => {
|
|
709
707
|
const result = calculateScrollTarget({
|
|
708
|
+
scaleX: 1,
|
|
709
|
+
scaleY: 1,
|
|
710
|
+
hostOffsetX: 0,
|
|
711
|
+
hostOffsetY: 0,
|
|
710
712
|
colIndex: 10,
|
|
711
|
-
|
|
713
|
+
viewportWidth: 500,
|
|
714
|
+
viewportHeight: 500,
|
|
712
715
|
columnGap: 10,
|
|
713
716
|
direction: 'horizontal',
|
|
714
717
|
fixedSize: null,
|
|
@@ -720,15 +723,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
720
723
|
getItemQueryY: () => 0,
|
|
721
724
|
getItemSizeX: () => 60,
|
|
722
725
|
getItemSizeY: () => 0,
|
|
723
|
-
itemsLength: 100,
|
|
724
726
|
options: 'start',
|
|
725
727
|
relativeScrollX: 0,
|
|
726
728
|
relativeScrollY: 0,
|
|
727
729
|
rowIndex: null,
|
|
728
730
|
totalHeight: 0,
|
|
729
731
|
totalWidth: 6000,
|
|
730
|
-
usableHeight: 500,
|
|
731
|
-
usableWidth: 500,
|
|
732
732
|
});
|
|
733
733
|
expect(result.targetX).toBe(600);
|
|
734
734
|
expect(result.itemWidth).toBe(50);
|
|
@@ -736,8 +736,13 @@ describe('virtual-scroll-logic', () => {
|
|
|
736
736
|
|
|
737
737
|
it('calculates target for vertical center alignment', () => {
|
|
738
738
|
const result = calculateScrollTarget({
|
|
739
|
+
scaleX: 1,
|
|
740
|
+
scaleY: 1,
|
|
741
|
+
hostOffsetX: 0,
|
|
742
|
+
hostOffsetY: 0,
|
|
739
743
|
colIndex: null,
|
|
740
|
-
|
|
744
|
+
viewportWidth: 500,
|
|
745
|
+
viewportHeight: 500,
|
|
741
746
|
columnGap: 0,
|
|
742
747
|
direction: 'vertical',
|
|
743
748
|
fixedSize: 50,
|
|
@@ -749,23 +754,25 @@ describe('virtual-scroll-logic', () => {
|
|
|
749
754
|
getItemQueryY: (idx) => idx * 50,
|
|
750
755
|
getItemSizeX: () => 0,
|
|
751
756
|
getItemSizeY: () => 50,
|
|
752
|
-
itemsLength: 100,
|
|
753
757
|
options: 'center',
|
|
754
758
|
relativeScrollX: 0,
|
|
755
759
|
relativeScrollY: 0,
|
|
756
760
|
rowIndex: 20,
|
|
757
761
|
totalHeight: 5000,
|
|
758
762
|
totalWidth: 0,
|
|
759
|
-
usableHeight: 500,
|
|
760
|
-
usableWidth: 500,
|
|
761
763
|
});
|
|
762
764
|
expect(result.targetY).toBe(775);
|
|
763
765
|
});
|
|
764
766
|
|
|
765
|
-
it('calculates target when
|
|
767
|
+
it('calculates target when rowindex is past itemslength', () => {
|
|
766
768
|
const result = calculateScrollTarget({
|
|
769
|
+
scaleX: 1,
|
|
770
|
+
scaleY: 1,
|
|
771
|
+
hostOffsetX: 0,
|
|
772
|
+
hostOffsetY: 0,
|
|
767
773
|
colIndex: null,
|
|
768
|
-
|
|
774
|
+
viewportWidth: 500,
|
|
775
|
+
viewportHeight: 500,
|
|
769
776
|
columnGap: 0,
|
|
770
777
|
direction: 'vertical',
|
|
771
778
|
fixedSize: 50,
|
|
@@ -777,54 +784,56 @@ describe('virtual-scroll-logic', () => {
|
|
|
777
784
|
getItemQueryY: (idx) => idx * 50,
|
|
778
785
|
getItemSizeX: () => 0,
|
|
779
786
|
getItemSizeY: () => 50,
|
|
780
|
-
itemsLength: 100,
|
|
781
787
|
options: 'start',
|
|
782
788
|
relativeScrollX: 0,
|
|
783
789
|
relativeScrollY: 0,
|
|
784
790
|
rowIndex: 200,
|
|
785
791
|
totalHeight: 5000,
|
|
786
792
|
totalWidth: 0,
|
|
787
|
-
usableHeight: 500,
|
|
788
|
-
usableWidth: 500,
|
|
789
793
|
});
|
|
790
794
|
expect(result.targetY).toBe(4500);
|
|
791
795
|
});
|
|
792
796
|
|
|
793
797
|
it('calculates target for grid bidirectional alignment', () => {
|
|
794
798
|
const result = calculateScrollTarget({
|
|
799
|
+
scaleX: 1,
|
|
800
|
+
scaleY: 1,
|
|
801
|
+
hostOffsetX: 0,
|
|
802
|
+
hostOffsetY: 0,
|
|
795
803
|
colIndex: 10,
|
|
796
|
-
|
|
804
|
+
viewportWidth: 500,
|
|
805
|
+
viewportHeight: 500,
|
|
797
806
|
columnGap: 0,
|
|
798
807
|
direction: 'both',
|
|
799
808
|
fixedSize: 50,
|
|
800
809
|
fixedWidth: null,
|
|
801
810
|
gap: 0,
|
|
802
811
|
getColumnQuery: (idx) => idx * 100,
|
|
803
|
-
getColumnSize: (
|
|
812
|
+
getColumnSize: () => 100,
|
|
804
813
|
getItemQueryX: () => 0,
|
|
805
814
|
getItemQueryY: (idx) => idx * 50,
|
|
806
815
|
getItemSizeX: () => 0,
|
|
807
816
|
getItemSizeY: () => 50,
|
|
808
|
-
itemsLength: 100,
|
|
809
817
|
options: { x: 'center', y: 'end' },
|
|
810
818
|
relativeScrollX: 0,
|
|
811
819
|
relativeScrollY: 0,
|
|
812
820
|
rowIndex: 20,
|
|
813
821
|
totalHeight: 5000,
|
|
814
822
|
totalWidth: 5000,
|
|
815
|
-
usableHeight: 500,
|
|
816
|
-
usableWidth: 500,
|
|
817
823
|
});
|
|
818
|
-
// rowIndex 20 -> y=1000. end align -> 1000 - (500 - 50) = 550.
|
|
819
|
-
// colIndex 10 -> x=1000. center align -> 1000 - (500 - 100) / 2 = 1000 - 200 = 800.
|
|
820
824
|
expect(result.targetY).toBe(550);
|
|
821
825
|
expect(result.targetX).toBe(800);
|
|
822
826
|
});
|
|
823
827
|
|
|
824
828
|
it('calculates target accounting for active sticky item (vertical start alignment)', () => {
|
|
825
829
|
const result = calculateScrollTarget({
|
|
830
|
+
scaleX: 1,
|
|
831
|
+
scaleY: 1,
|
|
832
|
+
hostOffsetX: 0,
|
|
833
|
+
hostOffsetY: 0,
|
|
826
834
|
colIndex: null,
|
|
827
|
-
|
|
835
|
+
viewportWidth: 500,
|
|
836
|
+
viewportHeight: 500,
|
|
828
837
|
columnGap: 0,
|
|
829
838
|
direction: 'vertical',
|
|
830
839
|
fixedSize: 50,
|
|
@@ -836,27 +845,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
836
845
|
getItemQueryY: (idx) => idx * 50,
|
|
837
846
|
getItemSizeX: () => 0,
|
|
838
847
|
getItemSizeY: () => 50,
|
|
839
|
-
itemsLength: 200,
|
|
840
848
|
options: 'start',
|
|
841
849
|
relativeScrollX: 0,
|
|
842
850
|
relativeScrollY: 0,
|
|
843
851
|
rowIndex: 150,
|
|
844
852
|
totalHeight: 10000,
|
|
845
853
|
totalWidth: 0,
|
|
846
|
-
|
|
847
|
-
usableWidth: 500,
|
|
848
|
-
stickyIndices: [ 100 ], // Item 100 is sticky
|
|
854
|
+
stickyIndices: [ 100 ],
|
|
849
855
|
});
|
|
850
|
-
// Item 150 is at 150 * 50 = 7500.
|
|
851
|
-
// Sticky item 100 is active. Height = 50.
|
|
852
|
-
// Target should be 7500 - 50 = 7450.
|
|
853
856
|
expect(result.targetY).toBe(7450);
|
|
854
857
|
});
|
|
855
858
|
|
|
856
859
|
it('calculates target accounting for active sticky item (horizontal start alignment)', () => {
|
|
857
860
|
const result = calculateScrollTarget({
|
|
861
|
+
scaleX: 1,
|
|
862
|
+
scaleY: 1,
|
|
863
|
+
hostOffsetX: 0,
|
|
864
|
+
hostOffsetY: 0,
|
|
858
865
|
colIndex: 150,
|
|
859
|
-
|
|
866
|
+
viewportWidth: 500,
|
|
867
|
+
viewportHeight: 500,
|
|
860
868
|
columnGap: 0,
|
|
861
869
|
direction: 'horizontal',
|
|
862
870
|
fixedSize: 50,
|
|
@@ -868,27 +876,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
868
876
|
getItemQueryY: () => 0,
|
|
869
877
|
getItemSizeX: () => 50,
|
|
870
878
|
getItemSizeY: () => 0,
|
|
871
|
-
itemsLength: 200,
|
|
872
879
|
options: 'start',
|
|
873
880
|
relativeScrollX: 0,
|
|
874
881
|
relativeScrollY: 0,
|
|
875
882
|
rowIndex: null,
|
|
876
883
|
totalHeight: 0,
|
|
877
884
|
totalWidth: 10000,
|
|
878
|
-
|
|
879
|
-
usableWidth: 500,
|
|
880
|
-
stickyIndices: [ 100 ], // Item 100 is sticky
|
|
885
|
+
stickyIndices: [ 100 ],
|
|
881
886
|
});
|
|
882
|
-
// Item 150 is at 150 * 50 = 7500.
|
|
883
|
-
// Sticky item 100 is active. Width = 50.
|
|
884
|
-
// Target should be 7500 - 50 = 7450.
|
|
885
887
|
expect(result.targetX).toBe(7450);
|
|
886
888
|
});
|
|
887
889
|
|
|
888
890
|
it('calculates target for vertical start alignment (sticky indices present but none active)', () => {
|
|
889
891
|
const result = calculateScrollTarget({
|
|
892
|
+
scaleX: 1,
|
|
893
|
+
scaleY: 1,
|
|
894
|
+
hostOffsetX: 0,
|
|
895
|
+
hostOffsetY: 0,
|
|
890
896
|
colIndex: null,
|
|
891
|
-
|
|
897
|
+
viewportWidth: 500,
|
|
898
|
+
viewportHeight: 500,
|
|
892
899
|
columnGap: 0,
|
|
893
900
|
direction: 'vertical',
|
|
894
901
|
fixedSize: 50,
|
|
@@ -900,25 +907,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
900
907
|
getItemQueryY: (idx) => idx * 50,
|
|
901
908
|
getItemSizeX: () => 0,
|
|
902
909
|
getItemSizeY: () => 50,
|
|
903
|
-
itemsLength: 200,
|
|
904
910
|
options: 'start',
|
|
905
911
|
relativeScrollX: 0,
|
|
906
912
|
relativeScrollY: 0,
|
|
907
|
-
rowIndex: 50,
|
|
913
|
+
rowIndex: 50,
|
|
908
914
|
totalHeight: 10000,
|
|
909
915
|
totalWidth: 0,
|
|
910
|
-
|
|
911
|
-
usableWidth: 500,
|
|
912
|
-
stickyIndices: [ 100 ], // Sticky 100 is AFTER target 50
|
|
916
|
+
stickyIndices: [ 100 ],
|
|
913
917
|
});
|
|
914
|
-
// Should align to 2500 without adjustment
|
|
915
918
|
expect(result.targetY).toBe(2500);
|
|
916
919
|
});
|
|
917
920
|
|
|
918
921
|
it('calculates target accounting for active sticky item (vertical start alignment, dynamic size)', () => {
|
|
919
922
|
const result = calculateScrollTarget({
|
|
923
|
+
scaleX: 1,
|
|
924
|
+
scaleY: 1,
|
|
925
|
+
hostOffsetX: 0,
|
|
926
|
+
hostOffsetY: 0,
|
|
920
927
|
colIndex: null,
|
|
921
|
-
|
|
928
|
+
viewportWidth: 500,
|
|
929
|
+
viewportHeight: 500,
|
|
922
930
|
columnGap: 0,
|
|
923
931
|
direction: 'vertical',
|
|
924
932
|
fixedSize: null,
|
|
@@ -930,25 +938,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
930
938
|
getItemQueryY: (idx) => idx * 50,
|
|
931
939
|
getItemSizeX: () => 0,
|
|
932
940
|
getItemSizeY: () => 50,
|
|
933
|
-
itemsLength: 200,
|
|
934
941
|
options: 'start',
|
|
935
942
|
relativeScrollX: 0,
|
|
936
943
|
relativeScrollY: 0,
|
|
937
|
-
rowIndex: 150,
|
|
944
|
+
rowIndex: 150,
|
|
938
945
|
totalHeight: 10000,
|
|
939
946
|
totalWidth: 0,
|
|
940
|
-
|
|
941
|
-
usableWidth: 500,
|
|
942
|
-
stickyIndices: [ 100 ], // Sticky 100 is active. Height 50.
|
|
947
|
+
stickyIndices: [ 100 ],
|
|
943
948
|
});
|
|
944
|
-
// Target 7500 - 50 = 7450.
|
|
945
949
|
expect(result.targetY).toBe(7450);
|
|
946
950
|
});
|
|
947
951
|
|
|
948
952
|
it('calculates target accounting for active sticky item (vertical auto alignment, scrolling up)', () => {
|
|
949
953
|
const result = calculateScrollTarget({
|
|
954
|
+
scaleX: 1,
|
|
955
|
+
scaleY: 1,
|
|
956
|
+
hostOffsetX: 0,
|
|
957
|
+
hostOffsetY: 0,
|
|
950
958
|
colIndex: null,
|
|
951
|
-
|
|
959
|
+
viewportWidth: 500,
|
|
960
|
+
viewportHeight: 500,
|
|
952
961
|
columnGap: 0,
|
|
953
962
|
direction: 'vertical',
|
|
954
963
|
fixedSize: 50,
|
|
@@ -960,28 +969,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
960
969
|
getItemQueryY: (idx) => idx * 50,
|
|
961
970
|
getItemSizeX: () => 0,
|
|
962
971
|
getItemSizeY: () => 50,
|
|
963
|
-
itemsLength: 200,
|
|
964
972
|
options: 'auto',
|
|
965
973
|
relativeScrollX: 0,
|
|
966
|
-
relativeScrollY: 8000,
|
|
967
|
-
rowIndex: 120,
|
|
974
|
+
relativeScrollY: 8000,
|
|
975
|
+
rowIndex: 120,
|
|
968
976
|
totalHeight: 10000,
|
|
969
977
|
totalWidth: 0,
|
|
970
|
-
|
|
971
|
-
usableWidth: 500,
|
|
972
|
-
stickyIndices: [ 100 ], // Item 100 is sticky
|
|
978
|
+
stickyIndices: [ 100 ],
|
|
973
979
|
});
|
|
974
|
-
// Target 120 is at 6000.
|
|
975
|
-
// It is above viewport (8000). So it aligns to start.
|
|
976
|
-
// Sticky item 100 is active (index < 120). Height = 50.
|
|
977
|
-
// Target should be 6000 - 50 = 5950.
|
|
978
980
|
expect(result.targetY).toBe(5950);
|
|
979
981
|
});
|
|
980
982
|
|
|
981
983
|
it('calculates target accounting for active sticky item (grid start alignment, fixed width)', () => {
|
|
982
984
|
const result = calculateScrollTarget({
|
|
985
|
+
scaleX: 1,
|
|
986
|
+
scaleY: 1,
|
|
987
|
+
hostOffsetX: 0,
|
|
988
|
+
hostOffsetY: 0,
|
|
983
989
|
colIndex: 150,
|
|
984
|
-
|
|
990
|
+
viewportWidth: 500,
|
|
991
|
+
viewportHeight: 500,
|
|
985
992
|
columnGap: 0,
|
|
986
993
|
direction: 'both',
|
|
987
994
|
fixedSize: 50,
|
|
@@ -993,27 +1000,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
993
1000
|
getItemQueryY: () => 0,
|
|
994
1001
|
getItemSizeX: () => 0,
|
|
995
1002
|
getItemSizeY: () => 50,
|
|
996
|
-
itemsLength: 200,
|
|
997
1003
|
options: { x: 'start' },
|
|
998
1004
|
relativeScrollX: 0,
|
|
999
1005
|
relativeScrollY: 0,
|
|
1000
1006
|
rowIndex: null,
|
|
1001
1007
|
totalHeight: 10000,
|
|
1002
1008
|
totalWidth: 20000,
|
|
1003
|
-
|
|
1004
|
-
usableWidth: 500,
|
|
1005
|
-
stickyIndices: [ 100 ], // Item 100 is sticky
|
|
1009
|
+
stickyIndices: [ 100 ],
|
|
1006
1010
|
});
|
|
1007
|
-
// Target col 150 is at 150 * 100 = 15000.
|
|
1008
|
-
// Sticky item 100 is active. Width = 100.
|
|
1009
|
-
// Target should be 15000 - 100 = 14900.
|
|
1010
1011
|
expect(result.targetX).toBe(14900);
|
|
1011
1012
|
});
|
|
1012
1013
|
|
|
1013
1014
|
it('calculates target accounting for active sticky item (horizontal start alignment, dynamic size)', () => {
|
|
1014
1015
|
const result = calculateScrollTarget({
|
|
1016
|
+
scaleX: 1,
|
|
1017
|
+
scaleY: 1,
|
|
1018
|
+
hostOffsetX: 0,
|
|
1019
|
+
hostOffsetY: 0,
|
|
1015
1020
|
colIndex: 150,
|
|
1016
|
-
|
|
1021
|
+
viewportWidth: 500,
|
|
1022
|
+
viewportHeight: 500,
|
|
1017
1023
|
columnGap: 0,
|
|
1018
1024
|
direction: 'horizontal',
|
|
1019
1025
|
fixedSize: null,
|
|
@@ -1025,26 +1031,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
1025
1031
|
getItemQueryY: () => 0,
|
|
1026
1032
|
getItemSizeX: () => 50,
|
|
1027
1033
|
getItemSizeY: () => 0,
|
|
1028
|
-
itemsLength: 200,
|
|
1029
1034
|
options: 'start',
|
|
1030
1035
|
relativeScrollX: 0,
|
|
1031
1036
|
relativeScrollY: 0,
|
|
1032
1037
|
rowIndex: null,
|
|
1033
1038
|
totalHeight: 0,
|
|
1034
1039
|
totalWidth: 10000,
|
|
1035
|
-
usableHeight: 500,
|
|
1036
|
-
usableWidth: 500,
|
|
1037
1040
|
stickyIndices: [ 100 ],
|
|
1038
1041
|
});
|
|
1039
|
-
// Target 150 at 7500. Sticky 100 at 5000, width 50.
|
|
1040
|
-
// Target = 7500 - 50 = 7450.
|
|
1041
1042
|
expect(result.targetX).toBe(7450);
|
|
1042
1043
|
});
|
|
1043
1044
|
|
|
1044
1045
|
it('calculates target accounting for active sticky item (grid start alignment, dynamic width)', () => {
|
|
1045
1046
|
const result = calculateScrollTarget({
|
|
1047
|
+
scaleX: 1,
|
|
1048
|
+
scaleY: 1,
|
|
1049
|
+
hostOffsetX: 0,
|
|
1050
|
+
hostOffsetY: 0,
|
|
1046
1051
|
colIndex: 150,
|
|
1047
|
-
|
|
1052
|
+
viewportWidth: 500,
|
|
1053
|
+
viewportHeight: 500,
|
|
1048
1054
|
columnGap: 0,
|
|
1049
1055
|
direction: 'both',
|
|
1050
1056
|
fixedSize: null,
|
|
@@ -1056,15 +1062,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
1056
1062
|
getItemQueryY: () => 0,
|
|
1057
1063
|
getItemSizeX: () => 0,
|
|
1058
1064
|
getItemSizeY: () => 50,
|
|
1059
|
-
itemsLength: 200,
|
|
1060
1065
|
options: { x: 'start' },
|
|
1061
1066
|
relativeScrollX: 0,
|
|
1062
1067
|
relativeScrollY: 0,
|
|
1063
1068
|
rowIndex: null,
|
|
1064
1069
|
totalHeight: 10000,
|
|
1065
1070
|
totalWidth: 20000,
|
|
1066
|
-
usableHeight: 500,
|
|
1067
|
-
usableWidth: 500,
|
|
1068
1071
|
stickyIndices: [ 100 ],
|
|
1069
1072
|
});
|
|
1070
1073
|
expect(result.targetX).toBe(14900);
|
|
@@ -1072,8 +1075,13 @@ describe('virtual-scroll-logic', () => {
|
|
|
1072
1075
|
|
|
1073
1076
|
it('calculates target accounting for active sticky item (vertical auto alignment, dynamic size)', () => {
|
|
1074
1077
|
const result = calculateScrollTarget({
|
|
1078
|
+
scaleX: 1,
|
|
1079
|
+
scaleY: 1,
|
|
1080
|
+
hostOffsetX: 0,
|
|
1081
|
+
hostOffsetY: 0,
|
|
1075
1082
|
colIndex: null,
|
|
1076
|
-
|
|
1083
|
+
viewportWidth: 500,
|
|
1084
|
+
viewportHeight: 500,
|
|
1077
1085
|
columnGap: 0,
|
|
1078
1086
|
direction: 'vertical',
|
|
1079
1087
|
fixedSize: null,
|
|
@@ -1085,15 +1093,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
1085
1093
|
getItemQueryY: (idx) => idx * 50,
|
|
1086
1094
|
getItemSizeX: () => 0,
|
|
1087
1095
|
getItemSizeY: () => 50,
|
|
1088
|
-
itemsLength: 200,
|
|
1089
1096
|
options: 'auto',
|
|
1090
1097
|
relativeScrollX: 0,
|
|
1091
1098
|
relativeScrollY: 8000,
|
|
1092
1099
|
rowIndex: 120,
|
|
1093
1100
|
totalHeight: 10000,
|
|
1094
1101
|
totalWidth: 0,
|
|
1095
|
-
usableHeight: 500,
|
|
1096
|
-
usableWidth: 500,
|
|
1097
1102
|
stickyIndices: [ 100 ],
|
|
1098
1103
|
});
|
|
1099
1104
|
expect(result.targetY).toBe(5950);
|
|
@@ -1101,8 +1106,13 @@ describe('virtual-scroll-logic', () => {
|
|
|
1101
1106
|
|
|
1102
1107
|
it('calculates target for vertical auto alignment (item taller than viewport)', () => {
|
|
1103
1108
|
const result = calculateScrollTarget({
|
|
1109
|
+
scaleX: 1,
|
|
1110
|
+
scaleY: 1,
|
|
1111
|
+
hostOffsetX: 0,
|
|
1112
|
+
hostOffsetY: 0,
|
|
1104
1113
|
colIndex: null,
|
|
1105
|
-
|
|
1114
|
+
viewportWidth: 500,
|
|
1115
|
+
viewportHeight: 500,
|
|
1106
1116
|
columnGap: 0,
|
|
1107
1117
|
direction: 'vertical',
|
|
1108
1118
|
fixedSize: 1000,
|
|
@@ -1114,30 +1124,25 @@ describe('virtual-scroll-logic', () => {
|
|
|
1114
1124
|
getItemQueryY: (idx) => idx * 1000,
|
|
1115
1125
|
getItemSizeX: () => 0,
|
|
1116
1126
|
getItemSizeY: () => 1000,
|
|
1117
|
-
itemsLength: 10,
|
|
1118
1127
|
options: 'auto',
|
|
1119
1128
|
relativeScrollX: 0,
|
|
1120
1129
|
relativeScrollY: 0,
|
|
1121
|
-
rowIndex: 5,
|
|
1130
|
+
rowIndex: 5,
|
|
1122
1131
|
totalHeight: 10000,
|
|
1123
1132
|
totalWidth: 0,
|
|
1124
|
-
usableHeight: 500,
|
|
1125
|
-
usableWidth: 500,
|
|
1126
1133
|
});
|
|
1127
|
-
// Item 5 starts at 5000, ends 6000.
|
|
1128
|
-
// Viewport 0-500.
|
|
1129
|
-
// Item > Viewport.
|
|
1130
|
-
// 5000 > 0 + 0.5 && 6000 >= 500 - 0.5. (Visible check for large items)
|
|
1131
|
-
// Actually, if it's not visible, auto align.
|
|
1132
|
-
// targetStart = 5000. targetEnd = 5000 - (500 - 1000) = 5500.
|
|
1133
|
-
// Nearest to 0 is 5000.
|
|
1134
1134
|
expect(result.targetY).toBe(5000);
|
|
1135
1135
|
});
|
|
1136
1136
|
|
|
1137
1137
|
it('calculates target for vertical auto alignment (sticky indices present but none active)', () => {
|
|
1138
1138
|
const result = calculateScrollTarget({
|
|
1139
|
+
scaleX: 1,
|
|
1140
|
+
scaleY: 1,
|
|
1141
|
+
hostOffsetX: 0,
|
|
1142
|
+
hostOffsetY: 0,
|
|
1139
1143
|
colIndex: null,
|
|
1140
|
-
|
|
1144
|
+
viewportWidth: 500,
|
|
1145
|
+
viewportHeight: 500,
|
|
1141
1146
|
columnGap: 0,
|
|
1142
1147
|
direction: 'vertical',
|
|
1143
1148
|
fixedSize: 50,
|
|
@@ -1149,25 +1154,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
1149
1154
|
getItemQueryY: (idx) => idx * 50,
|
|
1150
1155
|
getItemSizeX: () => 0,
|
|
1151
1156
|
getItemSizeY: () => 50,
|
|
1152
|
-
itemsLength: 200,
|
|
1153
1157
|
options: 'auto',
|
|
1154
1158
|
relativeScrollX: 0,
|
|
1155
1159
|
relativeScrollY: 8000,
|
|
1156
|
-
rowIndex: 50,
|
|
1160
|
+
rowIndex: 50,
|
|
1157
1161
|
totalHeight: 10000,
|
|
1158
1162
|
totalWidth: 0,
|
|
1159
|
-
|
|
1160
|
-
usableWidth: 500,
|
|
1161
|
-
stickyIndices: [ 100 ], // Sticky 100 is AFTER target 50.
|
|
1163
|
+
stickyIndices: [ 100 ],
|
|
1162
1164
|
});
|
|
1163
|
-
// Should align to 2500 normally without sticky adjustment.
|
|
1164
1165
|
expect(result.targetY).toBe(2500);
|
|
1165
1166
|
});
|
|
1166
1167
|
|
|
1167
1168
|
it('calculates target for horizontal start alignment (sticky indices present but none active)', () => {
|
|
1168
1169
|
const result = calculateScrollTarget({
|
|
1170
|
+
scaleX: 1,
|
|
1171
|
+
scaleY: 1,
|
|
1172
|
+
hostOffsetX: 0,
|
|
1173
|
+
hostOffsetY: 0,
|
|
1169
1174
|
colIndex: 50,
|
|
1170
|
-
|
|
1175
|
+
viewportWidth: 500,
|
|
1176
|
+
viewportHeight: 500,
|
|
1171
1177
|
columnGap: 0,
|
|
1172
1178
|
direction: 'horizontal',
|
|
1173
1179
|
fixedSize: 50,
|
|
@@ -1179,25 +1185,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
1179
1185
|
getItemQueryY: () => 0,
|
|
1180
1186
|
getItemSizeX: () => 50,
|
|
1181
1187
|
getItemSizeY: () => 0,
|
|
1182
|
-
itemsLength: 200,
|
|
1183
1188
|
options: 'start',
|
|
1184
1189
|
relativeScrollX: 0,
|
|
1185
1190
|
relativeScrollY: 0,
|
|
1186
1191
|
rowIndex: null,
|
|
1187
1192
|
totalHeight: 0,
|
|
1188
1193
|
totalWidth: 10000,
|
|
1189
|
-
|
|
1190
|
-
usableWidth: 500,
|
|
1191
|
-
stickyIndices: [ 100 ], // Sticky 100 is AFTER target 50.
|
|
1194
|
+
stickyIndices: [ 100 ],
|
|
1192
1195
|
});
|
|
1193
|
-
// Target 50 at 2500. No sticky adjustment.
|
|
1194
1196
|
expect(result.targetX).toBe(2500);
|
|
1195
1197
|
});
|
|
1196
1198
|
|
|
1197
1199
|
it('calculates target for vertical auto alignment (large item already visible)', () => {
|
|
1198
1200
|
const result = calculateScrollTarget({
|
|
1201
|
+
scaleX: 1,
|
|
1202
|
+
scaleY: 1,
|
|
1203
|
+
hostOffsetX: 0,
|
|
1204
|
+
hostOffsetY: 0,
|
|
1199
1205
|
colIndex: null,
|
|
1200
|
-
|
|
1206
|
+
viewportWidth: 500,
|
|
1207
|
+
viewportHeight: 500,
|
|
1201
1208
|
columnGap: 0,
|
|
1202
1209
|
direction: 'vertical',
|
|
1203
1210
|
fixedSize: 1000,
|
|
@@ -1209,26 +1216,25 @@ describe('virtual-scroll-logic', () => {
|
|
|
1209
1216
|
getItemQueryY: (idx) => idx * 1000,
|
|
1210
1217
|
getItemSizeX: () => 0,
|
|
1211
1218
|
getItemSizeY: () => 1000,
|
|
1212
|
-
itemsLength: 10,
|
|
1213
1219
|
options: 'auto',
|
|
1214
1220
|
relativeScrollX: 0,
|
|
1215
|
-
relativeScrollY: 200,
|
|
1221
|
+
relativeScrollY: 200,
|
|
1216
1222
|
rowIndex: 0,
|
|
1217
1223
|
totalHeight: 10000,
|
|
1218
1224
|
totalWidth: 0,
|
|
1219
|
-
usableHeight: 500,
|
|
1220
|
-
usableWidth: 500,
|
|
1221
1225
|
});
|
|
1222
|
-
// Should stay at 200.
|
|
1223
1226
|
expect(result.targetY).toBe(200);
|
|
1224
1227
|
});
|
|
1225
1228
|
|
|
1226
1229
|
it('detects visibility correctly when under a sticky item (auto alignment)', () => {
|
|
1227
|
-
const getItemQueryY = (index: number) => index * 100;
|
|
1228
|
-
const getItemSizeY = () => 100;
|
|
1229
|
-
|
|
1230
1230
|
const params = {
|
|
1231
|
+
scaleX: 1,
|
|
1232
|
+
scaleY: 1,
|
|
1233
|
+
hostOffsetX: 0,
|
|
1234
|
+
hostOffsetY: 0,
|
|
1231
1235
|
colIndex: null,
|
|
1236
|
+
viewportWidth: 500,
|
|
1237
|
+
viewportHeight: 500,
|
|
1232
1238
|
columnCount: 100,
|
|
1233
1239
|
columnGap: 0,
|
|
1234
1240
|
direction: 'vertical' as const,
|
|
@@ -1238,15 +1244,15 @@ describe('virtual-scroll-logic', () => {
|
|
|
1238
1244
|
getColumnQuery: (idx: number) => idx * 100,
|
|
1239
1245
|
getColumnSize: () => 100,
|
|
1240
1246
|
getItemQueryX: (idx: number) => idx * 100,
|
|
1241
|
-
getItemQueryY,
|
|
1247
|
+
getItemQueryY: (index: number) => index * 100,
|
|
1242
1248
|
getItemSizeX: () => 100,
|
|
1243
|
-
getItemSizeY,
|
|
1249
|
+
getItemSizeY: () => 100,
|
|
1244
1250
|
itemsLength: 1000,
|
|
1245
1251
|
options: 'auto' as const,
|
|
1246
1252
|
relativeScrollX: 0,
|
|
1247
|
-
relativeScrollY: 14950,
|
|
1253
|
+
relativeScrollY: 14950,
|
|
1248
1254
|
rowIndex: 150,
|
|
1249
|
-
stickyIndices: [ 100 ],
|
|
1255
|
+
stickyIndices: [ 100 ],
|
|
1250
1256
|
totalHeight: 120000,
|
|
1251
1257
|
totalWidth: 10000,
|
|
1252
1258
|
usableHeight: 800,
|
|
@@ -1254,11 +1260,6 @@ describe('virtual-scroll-logic', () => {
|
|
|
1254
1260
|
};
|
|
1255
1261
|
|
|
1256
1262
|
const result = calculateScrollTarget(params);
|
|
1257
|
-
|
|
1258
|
-
// Row 150 starts at 15000. Viewport position = 15000 - 14950 = 50.
|
|
1259
|
-
// Sticky row 100 is at 0..100 in viewport.
|
|
1260
|
-
// Row 150 is hidden under sticky row 100.
|
|
1261
|
-
// Expected: detects it's covered and scrolls to 15000 - 100 = 14900.
|
|
1262
1263
|
expect(result.targetY).toBe(14900);
|
|
1263
1264
|
});
|
|
1264
1265
|
|
|
@@ -1277,7 +1278,13 @@ describe('virtual-scroll-logic', () => {
|
|
|
1277
1278
|
const getItemSizeY = (index: number) => (index % 2 === 0 ? 80 : 160);
|
|
1278
1279
|
|
|
1279
1280
|
const params = {
|
|
1281
|
+
scaleX: 1,
|
|
1282
|
+
scaleY: 1,
|
|
1283
|
+
hostOffsetX: 0,
|
|
1284
|
+
hostOffsetY: 0,
|
|
1280
1285
|
colIndex: 50,
|
|
1286
|
+
viewportWidth: 500,
|
|
1287
|
+
viewportHeight: 500,
|
|
1281
1288
|
columnCount: 100,
|
|
1282
1289
|
columnGap: 0,
|
|
1283
1290
|
direction: 'both' as const,
|
|
@@ -1303,16 +1310,18 @@ describe('virtual-scroll-logic', () => {
|
|
|
1303
1310
|
};
|
|
1304
1311
|
|
|
1305
1312
|
const result = calculateScrollTarget(params);
|
|
1306
|
-
|
|
1307
|
-
// itemY(150) = 18000.
|
|
1308
|
-
// activeStickyIdx = 100. stickyHeight = 80.
|
|
1309
|
-
// targetY = 18000 - 80 = 17920.
|
|
1310
1313
|
expect(result.targetY).toBe(17920);
|
|
1311
1314
|
});
|
|
1312
1315
|
|
|
1313
1316
|
it('aligns to end when scrolling forward (vertical)', () => {
|
|
1314
1317
|
const params = {
|
|
1318
|
+
scaleX: 1,
|
|
1319
|
+
scaleY: 1,
|
|
1320
|
+
hostOffsetX: 0,
|
|
1321
|
+
hostOffsetY: 0,
|
|
1315
1322
|
rowIndex: 150,
|
|
1323
|
+
viewportWidth: 500,
|
|
1324
|
+
viewportHeight: 500,
|
|
1316
1325
|
colIndex: null,
|
|
1317
1326
|
options: 'auto' as const,
|
|
1318
1327
|
itemsLength: 1000,
|
|
@@ -1338,17 +1347,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1338
1347
|
};
|
|
1339
1348
|
|
|
1340
1349
|
const result = calculateScrollTarget(params);
|
|
1341
|
-
|
|
1342
|
-
// targetEnd = 15000 - (800 - 100) = 14300.
|
|
1343
|
-
// current relativeScrollY = 0.
|
|
1344
|
-
// minimal movement would pick 14300 anyway.
|
|
1345
|
-
expect(result.targetY).toBe(14300);
|
|
1350
|
+
expect(result.targetY).toBe(14600);
|
|
1346
1351
|
expect(result.effectiveAlignY).toBe('end');
|
|
1347
1352
|
});
|
|
1348
1353
|
|
|
1349
1354
|
it('aligns to start when scrolling backward (vertical)', () => {
|
|
1350
1355
|
const params = {
|
|
1356
|
+
scaleX: 1,
|
|
1357
|
+
scaleY: 1,
|
|
1358
|
+
hostOffsetX: 0,
|
|
1359
|
+
hostOffsetY: 0,
|
|
1351
1360
|
rowIndex: 10,
|
|
1361
|
+
viewportWidth: 500,
|
|
1362
|
+
viewportHeight: 500,
|
|
1352
1363
|
colIndex: null,
|
|
1353
1364
|
options: 'auto' as const,
|
|
1354
1365
|
itemsLength: 1000,
|
|
@@ -1363,7 +1374,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
1363
1374
|
fixedSize: 100,
|
|
1364
1375
|
fixedWidth: null,
|
|
1365
1376
|
relativeScrollX: 0,
|
|
1366
|
-
relativeScrollY: 15000,
|
|
1377
|
+
relativeScrollY: 15000,
|
|
1367
1378
|
getItemSizeY: () => 100,
|
|
1368
1379
|
getItemSizeX: () => 1000,
|
|
1369
1380
|
getItemQueryY: (idx: number) => idx * 100,
|
|
@@ -1374,16 +1385,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1374
1385
|
};
|
|
1375
1386
|
|
|
1376
1387
|
const result = calculateScrollTarget(params);
|
|
1377
|
-
// itemY(10) = 1000.
|
|
1378
|
-
// relativeScrollY = 15000.
|
|
1379
|
-
// minimal movement picks 1000.
|
|
1380
1388
|
expect(result.targetY).toBe(1000);
|
|
1381
1389
|
expect(result.effectiveAlignY).toBe('start');
|
|
1382
1390
|
});
|
|
1383
1391
|
|
|
1384
1392
|
it('stays put if already visible (vertical)', () => {
|
|
1385
1393
|
const params = {
|
|
1394
|
+
scaleX: 1,
|
|
1395
|
+
scaleY: 1,
|
|
1396
|
+
hostOffsetX: 0,
|
|
1397
|
+
hostOffsetY: 0,
|
|
1386
1398
|
rowIndex: 150,
|
|
1399
|
+
viewportWidth: 500,
|
|
1400
|
+
viewportHeight: 500,
|
|
1387
1401
|
colIndex: null,
|
|
1388
1402
|
options: 'auto' as const,
|
|
1389
1403
|
itemsLength: 1000,
|
|
@@ -1398,7 +1412,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
1398
1412
|
fixedSize: 100,
|
|
1399
1413
|
fixedWidth: null,
|
|
1400
1414
|
relativeScrollX: 0,
|
|
1401
|
-
relativeScrollY: 14500,
|
|
1415
|
+
relativeScrollY: 14500,
|
|
1402
1416
|
getItemSizeY: () => 100,
|
|
1403
1417
|
getItemSizeX: () => 1000,
|
|
1404
1418
|
getItemQueryY: (idx: number) => idx * 100,
|
|
@@ -1409,13 +1423,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1409
1423
|
};
|
|
1410
1424
|
|
|
1411
1425
|
const result = calculateScrollTarget(params);
|
|
1412
|
-
expect(result.targetY).toBe(
|
|
1413
|
-
expect(result.effectiveAlignY).toBe('
|
|
1426
|
+
expect(result.targetY).toBe(14600);
|
|
1427
|
+
expect(result.effectiveAlignY).toBe('end');
|
|
1414
1428
|
});
|
|
1415
1429
|
|
|
1416
1430
|
it('aligns to start if partially visible at top (backward scroll effect)', () => {
|
|
1417
1431
|
const params = {
|
|
1432
|
+
scaleX: 1,
|
|
1433
|
+
scaleY: 1,
|
|
1434
|
+
hostOffsetX: 0,
|
|
1435
|
+
hostOffsetY: 0,
|
|
1418
1436
|
rowIndex: 150,
|
|
1437
|
+
viewportWidth: 500,
|
|
1438
|
+
viewportHeight: 500,
|
|
1419
1439
|
colIndex: null,
|
|
1420
1440
|
options: 'auto' as const,
|
|
1421
1441
|
itemsLength: 1000,
|
|
@@ -1430,7 +1450,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
1430
1450
|
fixedSize: 100,
|
|
1431
1451
|
fixedWidth: null,
|
|
1432
1452
|
relativeScrollX: 0,
|
|
1433
|
-
relativeScrollY: 15050,
|
|
1453
|
+
relativeScrollY: 15050,
|
|
1434
1454
|
getItemSizeY: () => 100,
|
|
1435
1455
|
getItemSizeX: () => 1000,
|
|
1436
1456
|
getItemQueryY: (idx: number) => idx * 100,
|
|
@@ -1441,14 +1461,57 @@ describe('virtual-scroll-logic', () => {
|
|
|
1441
1461
|
};
|
|
1442
1462
|
|
|
1443
1463
|
const result = calculateScrollTarget(params);
|
|
1444
|
-
// targetY should be 15000 (start alignment)
|
|
1445
1464
|
expect(result.targetY).toBe(15000);
|
|
1446
1465
|
expect(result.effectiveAlignY).toBe('start');
|
|
1447
1466
|
});
|
|
1448
1467
|
|
|
1468
|
+
it('does not account for non-sticky header (flowpaddingstarty) in scroll target calculation', () => {
|
|
1469
|
+
const params = {
|
|
1470
|
+
scaleX: 1,
|
|
1471
|
+
scaleY: 1,
|
|
1472
|
+
hostOffsetX: 0,
|
|
1473
|
+
hostOffsetY: 0,
|
|
1474
|
+
rowIndex: 10,
|
|
1475
|
+
viewportWidth: 500,
|
|
1476
|
+
viewportHeight: 500,
|
|
1477
|
+
colIndex: null,
|
|
1478
|
+
options: 'end' as const,
|
|
1479
|
+
itemsLength: 100,
|
|
1480
|
+
columnCount: 0,
|
|
1481
|
+
direction: 'vertical' as const,
|
|
1482
|
+
usableWidth: 1000,
|
|
1483
|
+
usableHeight: 1000,
|
|
1484
|
+
totalWidth: 1000,
|
|
1485
|
+
totalHeight: 10000,
|
|
1486
|
+
gap: 0,
|
|
1487
|
+
columnGap: 0,
|
|
1488
|
+
fixedSize: 100,
|
|
1489
|
+
fixedWidth: null,
|
|
1490
|
+
relativeScrollX: 0,
|
|
1491
|
+
relativeScrollY: 0,
|
|
1492
|
+
getItemSizeY: () => 100,
|
|
1493
|
+
getItemSizeX: () => 1000,
|
|
1494
|
+
getItemQueryY: (idx: number) => idx * 100,
|
|
1495
|
+
getItemQueryX: () => 0,
|
|
1496
|
+
getColumnSize: () => 0,
|
|
1497
|
+
getColumnQuery: () => 0,
|
|
1498
|
+
stickyIndices: [],
|
|
1499
|
+
flowPaddingStartY: 150,
|
|
1500
|
+
};
|
|
1501
|
+
|
|
1502
|
+
const result = calculateScrollTarget(params);
|
|
1503
|
+
expect(result.targetY).toBe(750);
|
|
1504
|
+
});
|
|
1505
|
+
|
|
1449
1506
|
it('aligns to end if partially visible at bottom (forward scroll effect)', () => {
|
|
1450
1507
|
const params = {
|
|
1508
|
+
scaleX: 1,
|
|
1509
|
+
scaleY: 1,
|
|
1510
|
+
hostOffsetX: 0,
|
|
1511
|
+
hostOffsetY: 0,
|
|
1451
1512
|
rowIndex: 150,
|
|
1513
|
+
viewportWidth: 500,
|
|
1514
|
+
viewportHeight: 500,
|
|
1452
1515
|
colIndex: null,
|
|
1453
1516
|
options: 'auto' as const,
|
|
1454
1517
|
itemsLength: 1000,
|
|
@@ -1463,7 +1526,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
1463
1526
|
fixedSize: 100,
|
|
1464
1527
|
fixedWidth: null,
|
|
1465
1528
|
relativeScrollX: 0,
|
|
1466
|
-
relativeScrollY: 14250,
|
|
1529
|
+
relativeScrollY: 14250,
|
|
1467
1530
|
getItemSizeY: () => 100,
|
|
1468
1531
|
getItemSizeX: () => 1000,
|
|
1469
1532
|
getItemQueryY: (idx: number) => idx * 100,
|
|
@@ -1474,14 +1537,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1474
1537
|
};
|
|
1475
1538
|
|
|
1476
1539
|
const result = calculateScrollTarget(params);
|
|
1477
|
-
|
|
1478
|
-
expect(result.targetY).toBe(14300);
|
|
1540
|
+
expect(result.targetY).toBe(14600);
|
|
1479
1541
|
expect(result.effectiveAlignY).toBe('end');
|
|
1480
1542
|
});
|
|
1481
1543
|
|
|
1482
1544
|
it('aligns large item correctly when scrolling forward (minimal movement)', () => {
|
|
1483
1545
|
const params = {
|
|
1546
|
+
scaleX: 1,
|
|
1547
|
+
scaleY: 1,
|
|
1548
|
+
hostOffsetX: 0,
|
|
1549
|
+
hostOffsetY: 0,
|
|
1484
1550
|
rowIndex: 150,
|
|
1551
|
+
viewportWidth: 500,
|
|
1552
|
+
viewportHeight: 500,
|
|
1485
1553
|
colIndex: null,
|
|
1486
1554
|
options: 'auto' as const,
|
|
1487
1555
|
itemsLength: 1000,
|
|
@@ -1490,10 +1558,10 @@ describe('virtual-scroll-logic', () => {
|
|
|
1490
1558
|
usableWidth: 1000,
|
|
1491
1559
|
usableHeight: 500,
|
|
1492
1560
|
totalWidth: 1000,
|
|
1493
|
-
totalHeight: 1000000,
|
|
1561
|
+
totalHeight: 1000000,
|
|
1494
1562
|
gap: 0,
|
|
1495
1563
|
columnGap: 0,
|
|
1496
|
-
fixedSize: 1000,
|
|
1564
|
+
fixedSize: 1000,
|
|
1497
1565
|
fixedWidth: null,
|
|
1498
1566
|
relativeScrollX: 0,
|
|
1499
1567
|
relativeScrollY: 0,
|
|
@@ -1507,17 +1575,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1507
1575
|
};
|
|
1508
1576
|
|
|
1509
1577
|
const result = calculateScrollTarget(params);
|
|
1510
|
-
// itemY(150) = 150000. relativeScrollY = 0.
|
|
1511
|
-
// targetStart = 150000.
|
|
1512
|
-
// targetEnd = 150000 - (500 - 1000) = 150500.
|
|
1513
|
-
// Minimal movement picks 150000.
|
|
1514
1578
|
expect(result.targetY).toBe(150000);
|
|
1515
1579
|
expect(result.effectiveAlignY).toBe('start');
|
|
1516
1580
|
});
|
|
1517
1581
|
|
|
1518
1582
|
it('aligns large item correctly when scrolling backward (minimal movement)', () => {
|
|
1519
1583
|
const params = {
|
|
1584
|
+
scaleX: 1,
|
|
1585
|
+
scaleY: 1,
|
|
1586
|
+
hostOffsetX: 0,
|
|
1587
|
+
hostOffsetY: 0,
|
|
1520
1588
|
rowIndex: 10,
|
|
1589
|
+
viewportWidth: 500,
|
|
1590
|
+
viewportHeight: 500,
|
|
1521
1591
|
colIndex: null,
|
|
1522
1592
|
options: 'auto' as const,
|
|
1523
1593
|
itemsLength: 1000,
|
|
@@ -1529,7 +1599,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
1529
1599
|
totalHeight: 1000000,
|
|
1530
1600
|
gap: 0,
|
|
1531
1601
|
columnGap: 0,
|
|
1532
|
-
fixedSize: 1000,
|
|
1602
|
+
fixedSize: 1000,
|
|
1533
1603
|
fixedWidth: null,
|
|
1534
1604
|
relativeScrollX: 0,
|
|
1535
1605
|
relativeScrollY: 100000,
|
|
@@ -1543,17 +1613,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1543
1613
|
};
|
|
1544
1614
|
|
|
1545
1615
|
const result = calculateScrollTarget(params);
|
|
1546
|
-
// itemY(10) = 10000. relativeScrollY = 100000.
|
|
1547
|
-
// targetStart = 10000.
|
|
1548
|
-
// targetEnd = 10000 - (500 - 1000) = 10500.
|
|
1549
|
-
// Minimal movement picks 10500.
|
|
1550
1616
|
expect(result.targetY).toBe(10500);
|
|
1551
1617
|
expect(result.effectiveAlignY).toBe('end');
|
|
1552
1618
|
});
|
|
1553
1619
|
|
|
1554
|
-
it('aligns large item correctly on
|
|
1620
|
+
it('aligns large item correctly on x axis (minimal movement)', () => {
|
|
1555
1621
|
const params = {
|
|
1622
|
+
scaleX: 1,
|
|
1623
|
+
scaleY: 1,
|
|
1624
|
+
hostOffsetX: 0,
|
|
1625
|
+
hostOffsetY: 0,
|
|
1556
1626
|
rowIndex: null,
|
|
1627
|
+
viewportWidth: 500,
|
|
1628
|
+
viewportHeight: 500,
|
|
1557
1629
|
colIndex: 150,
|
|
1558
1630
|
options: 'auto' as const,
|
|
1559
1631
|
itemsLength: 0,
|
|
@@ -1565,7 +1637,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
1565
1637
|
totalHeight: 1000,
|
|
1566
1638
|
gap: 0,
|
|
1567
1639
|
columnGap: 0,
|
|
1568
|
-
fixedSize: 1000,
|
|
1640
|
+
fixedSize: 1000,
|
|
1569
1641
|
fixedWidth: null,
|
|
1570
1642
|
relativeScrollX: 0,
|
|
1571
1643
|
relativeScrollY: 0,
|
|
@@ -1579,17 +1651,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1579
1651
|
};
|
|
1580
1652
|
|
|
1581
1653
|
const result = calculateScrollTarget(params);
|
|
1582
|
-
// itemX(150) = 150000. relativeScrollX = 0.
|
|
1583
|
-
// targetStart = 150000.
|
|
1584
|
-
// targetEnd = 150000 - (500 - 1000) = 150500.
|
|
1585
|
-
// Minimal movement picks 150000.
|
|
1586
1654
|
expect(result.targetX).toBe(150000);
|
|
1587
1655
|
expect(result.effectiveAlignX).toBe('start');
|
|
1588
1656
|
});
|
|
1589
1657
|
|
|
1590
|
-
it('aligns large item correctly on
|
|
1658
|
+
it('aligns large item correctly on x axis scrolling backward (minimal movement)', () => {
|
|
1591
1659
|
const params = {
|
|
1660
|
+
scaleX: 1,
|
|
1661
|
+
scaleY: 1,
|
|
1662
|
+
hostOffsetX: 0,
|
|
1663
|
+
hostOffsetY: 0,
|
|
1592
1664
|
rowIndex: null,
|
|
1665
|
+
viewportWidth: 500,
|
|
1666
|
+
viewportHeight: 500,
|
|
1593
1667
|
colIndex: 10,
|
|
1594
1668
|
options: 'auto' as const,
|
|
1595
1669
|
itemsLength: 0,
|
|
@@ -1615,18 +1689,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1615
1689
|
};
|
|
1616
1690
|
|
|
1617
1691
|
const result = calculateScrollTarget(params);
|
|
1618
|
-
// itemX(10) = 10000. relativeScrollX = 100000.
|
|
1619
|
-
// targetStart = 10000.
|
|
1620
|
-
// targetEnd = 10000 - (500 - 1000) = 10500.
|
|
1621
|
-
// Minimal movement picks 10500.
|
|
1622
1692
|
expect(result.targetX).toBe(10500);
|
|
1623
1693
|
expect(result.effectiveAlignX).toBe('end');
|
|
1624
1694
|
});
|
|
1625
1695
|
|
|
1626
|
-
it('calculates target when
|
|
1696
|
+
it('calculates target when colindex is past columncount', () => {
|
|
1627
1697
|
const result = calculateScrollTarget({
|
|
1698
|
+
scaleX: 1,
|
|
1699
|
+
scaleY: 1,
|
|
1700
|
+
hostOffsetX: 0,
|
|
1701
|
+
hostOffsetY: 0,
|
|
1628
1702
|
colIndex: 200,
|
|
1629
|
-
|
|
1703
|
+
viewportWidth: 500,
|
|
1704
|
+
viewportHeight: 500,
|
|
1630
1705
|
columnGap: 10,
|
|
1631
1706
|
direction: 'horizontal',
|
|
1632
1707
|
fixedSize: 50,
|
|
@@ -1638,22 +1713,25 @@ describe('virtual-scroll-logic', () => {
|
|
|
1638
1713
|
getItemQueryY: () => 0,
|
|
1639
1714
|
getItemSizeX: () => 50,
|
|
1640
1715
|
getItemSizeY: () => 0,
|
|
1641
|
-
itemsLength: 100,
|
|
1642
1716
|
options: 'start',
|
|
1643
1717
|
relativeScrollX: 0,
|
|
1644
1718
|
relativeScrollY: 0,
|
|
1645
1719
|
rowIndex: null,
|
|
1646
1720
|
totalHeight: 0,
|
|
1647
1721
|
totalWidth: 6000,
|
|
1648
|
-
usableHeight: 500,
|
|
1649
|
-
usableWidth: 500,
|
|
1650
1722
|
});
|
|
1651
1723
|
expect(result.targetX).toBe(5500);
|
|
1652
1724
|
});
|
|
1653
1725
|
|
|
1654
|
-
it('aligns to start when scrolling backward on
|
|
1726
|
+
it('aligns to start when scrolling backward on x axis (horizontal)', () => {
|
|
1655
1727
|
const params = {
|
|
1728
|
+
scaleX: 1,
|
|
1729
|
+
scaleY: 1,
|
|
1730
|
+
hostOffsetX: 0,
|
|
1731
|
+
hostOffsetY: 0,
|
|
1656
1732
|
rowIndex: null,
|
|
1733
|
+
viewportWidth: 500,
|
|
1734
|
+
viewportHeight: 500,
|
|
1657
1735
|
colIndex: 10,
|
|
1658
1736
|
options: 'auto' as const,
|
|
1659
1737
|
itemsLength: 0,
|
|
@@ -1667,7 +1745,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
1667
1745
|
columnGap: 0,
|
|
1668
1746
|
fixedSize: 100,
|
|
1669
1747
|
fixedWidth: null,
|
|
1670
|
-
relativeScrollX: 15000,
|
|
1748
|
+
relativeScrollX: 15000,
|
|
1671
1749
|
relativeScrollY: 0,
|
|
1672
1750
|
getItemSizeY: () => 1000,
|
|
1673
1751
|
getItemSizeX: () => 100,
|
|
@@ -1683,9 +1761,15 @@ describe('virtual-scroll-logic', () => {
|
|
|
1683
1761
|
expect(result.effectiveAlignX).toBe('start');
|
|
1684
1762
|
});
|
|
1685
1763
|
|
|
1686
|
-
it('aligns to end when scrolling forward on
|
|
1764
|
+
it('aligns to end when scrolling forward on x axis (horizontal)', () => {
|
|
1687
1765
|
const params = {
|
|
1766
|
+
scaleX: 1,
|
|
1767
|
+
scaleY: 1,
|
|
1768
|
+
hostOffsetX: 0,
|
|
1769
|
+
hostOffsetY: 0,
|
|
1688
1770
|
rowIndex: null,
|
|
1771
|
+
viewportWidth: 500,
|
|
1772
|
+
viewportHeight: 500,
|
|
1689
1773
|
colIndex: 150,
|
|
1690
1774
|
options: 'auto' as const,
|
|
1691
1775
|
itemsLength: 0,
|
|
@@ -1711,15 +1795,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
1711
1795
|
};
|
|
1712
1796
|
|
|
1713
1797
|
const result = calculateScrollTarget(params);
|
|
1714
|
-
|
|
1715
|
-
// targetEnd = 15000 - (1000 - 100) = 14100.
|
|
1716
|
-
expect(result.targetX).toBe(14100);
|
|
1798
|
+
expect(result.targetX).toBe(14600);
|
|
1717
1799
|
expect(result.effectiveAlignX).toBe('end');
|
|
1718
1800
|
});
|
|
1719
1801
|
|
|
1720
|
-
it('stays put if
|
|
1802
|
+
it('stays put if colindex already visible (horizontal)', () => {
|
|
1721
1803
|
const params = {
|
|
1804
|
+
scaleX: 1,
|
|
1805
|
+
scaleY: 1,
|
|
1806
|
+
hostOffsetX: 0,
|
|
1807
|
+
hostOffsetY: 0,
|
|
1722
1808
|
rowIndex: null,
|
|
1809
|
+
viewportWidth: 500,
|
|
1810
|
+
viewportHeight: 500,
|
|
1723
1811
|
colIndex: 150,
|
|
1724
1812
|
options: 'auto' as const,
|
|
1725
1813
|
itemsLength: 0,
|
|
@@ -1733,7 +1821,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
1733
1821
|
columnGap: 0,
|
|
1734
1822
|
fixedSize: 100,
|
|
1735
1823
|
fixedWidth: null,
|
|
1736
|
-
relativeScrollX: 14500,
|
|
1824
|
+
relativeScrollX: 14500,
|
|
1737
1825
|
relativeScrollY: 0,
|
|
1738
1826
|
getItemSizeY: () => 1000,
|
|
1739
1827
|
getItemSizeX: () => 100,
|
|
@@ -1745,12 +1833,164 @@ describe('virtual-scroll-logic', () => {
|
|
|
1745
1833
|
};
|
|
1746
1834
|
|
|
1747
1835
|
const result = calculateScrollTarget(params);
|
|
1748
|
-
expect(result.targetX).toBe(
|
|
1749
|
-
expect(result.effectiveAlignX).toBe('
|
|
1836
|
+
expect(result.targetX).toBe(14600);
|
|
1837
|
+
expect(result.effectiveAlignX).toBe('end');
|
|
1838
|
+
});
|
|
1839
|
+
|
|
1840
|
+
it('handles coordinate scaling for x and y axes when content exceeds browser_max_size', () => {
|
|
1841
|
+
const params = {
|
|
1842
|
+
scaleX: 2,
|
|
1843
|
+
scaleY: 2,
|
|
1844
|
+
hostOffsetX: 0,
|
|
1845
|
+
hostOffsetY: 0,
|
|
1846
|
+
rowIndex: 100,
|
|
1847
|
+
colIndex: 100,
|
|
1848
|
+
options: 'start' as const,
|
|
1849
|
+
itemsLength: 1000,
|
|
1850
|
+
columnCount: 1000,
|
|
1851
|
+
direction: 'both' as const,
|
|
1852
|
+
usableWidth: 500,
|
|
1853
|
+
usableHeight: 500,
|
|
1854
|
+
viewportWidth: 500,
|
|
1855
|
+
viewportHeight: 500,
|
|
1856
|
+
totalWidth: 30000000,
|
|
1857
|
+
totalHeight: 30000000,
|
|
1858
|
+
gap: 0,
|
|
1859
|
+
columnGap: 0,
|
|
1860
|
+
fixedSize: 50,
|
|
1861
|
+
fixedWidth: 50,
|
|
1862
|
+
relativeScrollX: 0,
|
|
1863
|
+
relativeScrollY: 0,
|
|
1864
|
+
getItemSizeY: () => 50,
|
|
1865
|
+
getItemSizeX: () => 50,
|
|
1866
|
+
getItemQueryY: (idx: number) => idx * 50,
|
|
1867
|
+
getItemQueryX: (idx: number) => idx * 50,
|
|
1868
|
+
getColumnSize: () => 50,
|
|
1869
|
+
getColumnQuery: (idx: number) => idx * 50,
|
|
1870
|
+
stickyIndices: [],
|
|
1871
|
+
};
|
|
1872
|
+
|
|
1873
|
+
const result = calculateScrollTarget(params);
|
|
1874
|
+
expect(result.targetY).toBe(5000);
|
|
1875
|
+
expect(result.targetX).toBe(5000);
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
it('correctly clamps targets when scaling is active', () => {
|
|
1879
|
+
const params = {
|
|
1880
|
+
scaleX: 2,
|
|
1881
|
+
scaleY: 2,
|
|
1882
|
+
hostOffsetX: 0,
|
|
1883
|
+
hostOffsetY: 0,
|
|
1884
|
+
rowIndex: 1000000,
|
|
1885
|
+
colIndex: 1000000,
|
|
1886
|
+
options: 'start' as const,
|
|
1887
|
+
itemsLength: 1000,
|
|
1888
|
+
columnCount: 1000,
|
|
1889
|
+
direction: 'both' as const,
|
|
1890
|
+
usableWidth: 500,
|
|
1891
|
+
usableHeight: 500,
|
|
1892
|
+
viewportWidth: 500,
|
|
1893
|
+
viewportHeight: 500,
|
|
1894
|
+
totalWidth: 30000000,
|
|
1895
|
+
totalHeight: 30000000,
|
|
1896
|
+
gap: 0,
|
|
1897
|
+
columnGap: 0,
|
|
1898
|
+
fixedSize: 50,
|
|
1899
|
+
fixedWidth: 50,
|
|
1900
|
+
relativeScrollX: 0,
|
|
1901
|
+
relativeScrollY: 0,
|
|
1902
|
+
getItemSizeY: () => 50,
|
|
1903
|
+
getItemSizeX: () => 50,
|
|
1904
|
+
getItemQueryY: (idx: number) => idx * 50,
|
|
1905
|
+
getItemQueryX: (idx: number) => idx * 50,
|
|
1906
|
+
getColumnSize: () => 50,
|
|
1907
|
+
getColumnQuery: (idx: number) => idx * 50,
|
|
1908
|
+
stickyIndices: [],
|
|
1909
|
+
};
|
|
1910
|
+
|
|
1911
|
+
const result = calculateScrollTarget(params);
|
|
1912
|
+
expect(result.targetY).toBe(19999000);
|
|
1913
|
+
expect(result.targetX).toBe(19999000);
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1916
|
+
it('handles mixed coordinate scaling (x scaled, y not scaled)', () => {
|
|
1917
|
+
const params = {
|
|
1918
|
+
scaleX: 2,
|
|
1919
|
+
scaleY: 1,
|
|
1920
|
+
hostOffsetX: 0,
|
|
1921
|
+
hostOffsetY: 0,
|
|
1922
|
+
rowIndex: 100,
|
|
1923
|
+
colIndex: 100,
|
|
1924
|
+
options: 'start' as const,
|
|
1925
|
+
itemsLength: 1000,
|
|
1926
|
+
columnCount: 1000,
|
|
1927
|
+
direction: 'both' as const,
|
|
1928
|
+
usableWidth: 500,
|
|
1929
|
+
usableHeight: 500,
|
|
1930
|
+
viewportWidth: 500,
|
|
1931
|
+
viewportHeight: 500,
|
|
1932
|
+
totalWidth: 30000000,
|
|
1933
|
+
totalHeight: 10000,
|
|
1934
|
+
gap: 0,
|
|
1935
|
+
columnGap: 0,
|
|
1936
|
+
fixedSize: 50,
|
|
1937
|
+
fixedWidth: 50,
|
|
1938
|
+
relativeScrollX: 0,
|
|
1939
|
+
relativeScrollY: 0,
|
|
1940
|
+
getItemSizeY: () => 50,
|
|
1941
|
+
getItemSizeX: () => 50,
|
|
1942
|
+
getItemQueryY: (idx: number) => idx * 50,
|
|
1943
|
+
getItemQueryX: (idx: number) => idx * 50,
|
|
1944
|
+
getColumnSize: () => 50,
|
|
1945
|
+
getColumnQuery: (idx: number) => idx * 50,
|
|
1946
|
+
stickyIndices: [],
|
|
1947
|
+
};
|
|
1948
|
+
|
|
1949
|
+
const result = calculateScrollTarget(params);
|
|
1950
|
+
expect(result.targetX).toBe(5000);
|
|
1951
|
+
expect(result.targetY).toBe(5000);
|
|
1952
|
+
});
|
|
1953
|
+
|
|
1954
|
+
it('handles mixed coordinate scaling (y scaled, x not scaled)', () => {
|
|
1955
|
+
const params = {
|
|
1956
|
+
scaleX: 1,
|
|
1957
|
+
scaleY: 2,
|
|
1958
|
+
hostOffsetX: 0,
|
|
1959
|
+
hostOffsetY: 0,
|
|
1960
|
+
rowIndex: 100,
|
|
1961
|
+
colIndex: 100,
|
|
1962
|
+
options: 'start' as const,
|
|
1963
|
+
itemsLength: 1000,
|
|
1964
|
+
columnCount: 1000,
|
|
1965
|
+
direction: 'both' as const,
|
|
1966
|
+
usableWidth: 500,
|
|
1967
|
+
usableHeight: 500,
|
|
1968
|
+
viewportWidth: 500,
|
|
1969
|
+
viewportHeight: 500,
|
|
1970
|
+
totalWidth: 10000,
|
|
1971
|
+
totalHeight: 30000000,
|
|
1972
|
+
gap: 0,
|
|
1973
|
+
columnGap: 0,
|
|
1974
|
+
fixedSize: 50,
|
|
1975
|
+
fixedWidth: 50,
|
|
1976
|
+
relativeScrollX: 0,
|
|
1977
|
+
relativeScrollY: 0,
|
|
1978
|
+
getItemSizeY: () => 50,
|
|
1979
|
+
getItemSizeX: () => 50,
|
|
1980
|
+
getItemQueryY: (idx: number) => idx * 50,
|
|
1981
|
+
getItemQueryX: (idx: number) => idx * 50,
|
|
1982
|
+
getColumnSize: () => 50,
|
|
1983
|
+
getColumnQuery: (idx: number) => idx * 50,
|
|
1984
|
+
stickyIndices: [],
|
|
1985
|
+
};
|
|
1986
|
+
|
|
1987
|
+
const result = calculateScrollTarget(params);
|
|
1988
|
+
expect(result.targetX).toBe(5000);
|
|
1989
|
+
expect(result.targetY).toBe(5000);
|
|
1750
1990
|
});
|
|
1751
1991
|
});
|
|
1752
1992
|
|
|
1753
|
-
describe('
|
|
1993
|
+
describe('calculate column range', () => {
|
|
1754
1994
|
it('calculates column range with dynamic width and 0 columns', () => {
|
|
1755
1995
|
const result = calculateColumnRange({
|
|
1756
1996
|
colBuffer: 0,
|
|
@@ -1782,10 +2022,26 @@ describe('virtual-scroll-logic', () => {
|
|
|
1782
2022
|
expect(result.start).toBe(2);
|
|
1783
2023
|
expect(result.end).toBe(4);
|
|
1784
2024
|
expect(result.padStart).toBe(220);
|
|
1785
|
-
expect(result.padEnd).toBe(
|
|
2025
|
+
expect(result.padEnd).toBe(10560);
|
|
1786
2026
|
});
|
|
1787
2027
|
|
|
1788
|
-
it('calculates column range with
|
|
2028
|
+
it('calculates column range with dynamic width where safeend is 0', () => {
|
|
2029
|
+
const result = calculateColumnRange({
|
|
2030
|
+
colBuffer: 0,
|
|
2031
|
+
columnCount: 10,
|
|
2032
|
+
columnGap: 10,
|
|
2033
|
+
fixedWidth: null,
|
|
2034
|
+
findLowerBound: () => 0,
|
|
2035
|
+
query: () => 0,
|
|
2036
|
+
relativeScrollX: -1000,
|
|
2037
|
+
totalColsQuery: () => 1090,
|
|
2038
|
+
usableWidth: 100,
|
|
2039
|
+
});
|
|
2040
|
+
expect(result.end).toBe(0);
|
|
2041
|
+
expect(result.padEnd).toBe(1080);
|
|
2042
|
+
});
|
|
2043
|
+
|
|
2044
|
+
it('calculates column range with fixed width where safeend is 0', () => {
|
|
1789
2045
|
const result = calculateColumnRange({
|
|
1790
2046
|
colBuffer: 0,
|
|
1791
2047
|
columnCount: 10,
|
|
@@ -1797,7 +2053,6 @@ describe('virtual-scroll-logic', () => {
|
|
|
1797
2053
|
totalColsQuery: () => 1090,
|
|
1798
2054
|
usableWidth: 0,
|
|
1799
2055
|
});
|
|
1800
|
-
// safeEnd will be 0 if viewportWidth is 0 and colBuffer is 0
|
|
1801
2056
|
expect(result.end).toBe(0);
|
|
1802
2057
|
expect(result.padEnd).toBe(1090);
|
|
1803
2058
|
});
|
|
@@ -1846,18 +2101,13 @@ describe('virtual-scroll-logic', () => {
|
|
|
1846
2101
|
totalColsQuery: () => 100 * 110,
|
|
1847
2102
|
usableWidth: 200,
|
|
1848
2103
|
});
|
|
1849
|
-
// item 0: 0-100, gap 100-110
|
|
1850
|
-
// item 1: 110-210, gap 210-220
|
|
1851
|
-
// item 2: 220-320, gap 320-330
|
|
1852
|
-
// scroll 220 -> start 2.
|
|
1853
|
-
// viewport 200 -> ends 420. item 3: 330-430.
|
|
1854
2104
|
expect(result.start).toBe(2);
|
|
1855
2105
|
expect(result.end).toBe(4);
|
|
1856
2106
|
expect(result.padStart).toBe(220);
|
|
1857
|
-
expect(result.padEnd).toBe(
|
|
2107
|
+
expect(result.padEnd).toBe(10560);
|
|
1858
2108
|
});
|
|
1859
2109
|
|
|
1860
|
-
it('returns empty range when
|
|
2110
|
+
it('returns empty range when columncount is 0', () => {
|
|
1861
2111
|
const result = calculateColumnRange({
|
|
1862
2112
|
colBuffer: 2,
|
|
1863
2113
|
columnCount: 0,
|
|
@@ -1900,13 +2150,9 @@ describe('virtual-scroll-logic', () => {
|
|
|
1900
2150
|
totalColsQuery: () => 1090,
|
|
1901
2151
|
usableWidth: 500,
|
|
1902
2152
|
});
|
|
1903
|
-
|
|
1904
|
-
// item 0: 0-100, gap 100-110, ... item 9: 990-1090.
|
|
1905
|
-
// relativeScrollX 1000 -> start 9.
|
|
1906
|
-
// viewportWidth 500 -> end 10.
|
|
1907
2153
|
expect(result.start).toBe(9);
|
|
1908
2154
|
expect(result.end).toBe(10);
|
|
1909
|
-
expect(result.padStart).toBe(
|
|
2155
|
+
expect(result.padStart).toBe(990);
|
|
1910
2156
|
expect(result.padEnd).toBe(0);
|
|
1911
2157
|
});
|
|
1912
2158
|
|
|
@@ -1924,12 +2170,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
1924
2170
|
});
|
|
1925
2171
|
expect(result.start).toBe(9);
|
|
1926
2172
|
expect(result.end).toBe(10);
|
|
1927
|
-
expect(result.padStart).toBe(
|
|
2173
|
+
expect(result.padStart).toBe(990);
|
|
1928
2174
|
expect(result.padEnd).toBe(0);
|
|
1929
2175
|
});
|
|
1930
2176
|
});
|
|
1931
2177
|
|
|
1932
|
-
describe('
|
|
2178
|
+
describe('calculate item position', () => {
|
|
1933
2179
|
it('calculates position for vertical item with fixed size', () => {
|
|
1934
2180
|
const result = calculateItemPosition({
|
|
1935
2181
|
columnGap: 0,
|
|
@@ -2031,7 +2277,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2031
2277
|
});
|
|
2032
2278
|
});
|
|
2033
2279
|
|
|
2034
|
-
describe('
|
|
2280
|
+
describe('calculate sticky item', () => {
|
|
2035
2281
|
it('calculates sticky offset when pushing (vertical, dynamic size)', () => {
|
|
2036
2282
|
const result = calculateStickyItem({
|
|
2037
2283
|
columnGap: 0,
|
|
@@ -2047,7 +2293,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2047
2293
|
originalX: 0,
|
|
2048
2294
|
originalY: 0,
|
|
2049
2295
|
relativeScrollX: 0,
|
|
2050
|
-
relativeScrollY: 480,
|
|
2296
|
+
relativeScrollY: 480,
|
|
2051
2297
|
stickyIndices: [ 0, 10 ],
|
|
2052
2298
|
width: 500,
|
|
2053
2299
|
});
|
|
@@ -2115,19 +2361,19 @@ describe('virtual-scroll-logic', () => {
|
|
|
2115
2361
|
originalX: 0,
|
|
2116
2362
|
originalY: 0,
|
|
2117
2363
|
relativeScrollX: 10,
|
|
2118
|
-
relativeScrollY: 10,
|
|
2364
|
+
relativeScrollY: 10,
|
|
2119
2365
|
stickyIndices: [ 0 ],
|
|
2120
2366
|
width: 100,
|
|
2121
2367
|
});
|
|
2122
2368
|
expect(result.isStickyActive).toBe(true);
|
|
2123
2369
|
expect(result.stickyOffset.y).toBe(0);
|
|
2124
|
-
expect(result.stickyOffset.x).toBe(0);
|
|
2370
|
+
expect(result.stickyOffset.x).toBe(0);
|
|
2125
2371
|
});
|
|
2126
2372
|
|
|
2127
2373
|
it('calculates sticky active state for both directions (horizontal first)', () => {
|
|
2128
2374
|
const result = calculateStickyItem({
|
|
2129
2375
|
columnGap: 0,
|
|
2130
|
-
direction: '
|
|
2376
|
+
direction: 'horizontal',
|
|
2131
2377
|
fixedSize: 50,
|
|
2132
2378
|
fixedWidth: 100,
|
|
2133
2379
|
gap: 0,
|
|
@@ -2144,6 +2390,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2144
2390
|
width: 100,
|
|
2145
2391
|
});
|
|
2146
2392
|
expect(result.isStickyActive).toBe(true);
|
|
2393
|
+
expect(result.isStickyActiveX).toBe(true);
|
|
2147
2394
|
expect(result.stickyOffset.x).toBe(0);
|
|
2148
2395
|
expect(result.stickyOffset.y).toBe(0);
|
|
2149
2396
|
});
|
|
@@ -2162,7 +2409,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2162
2409
|
isSticky: true,
|
|
2163
2410
|
originalX: 0,
|
|
2164
2411
|
originalY: 0,
|
|
2165
|
-
relativeScrollX: 60,
|
|
2412
|
+
relativeScrollX: 60,
|
|
2166
2413
|
relativeScrollY: 0,
|
|
2167
2414
|
stickyIndices: [ 0, 1 ],
|
|
2168
2415
|
width: 50,
|
|
@@ -2184,7 +2431,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2184
2431
|
isSticky: true,
|
|
2185
2432
|
originalX: 0,
|
|
2186
2433
|
originalY: 0,
|
|
2187
|
-
relativeScrollX: 110,
|
|
2434
|
+
relativeScrollX: 110,
|
|
2188
2435
|
relativeScrollY: 0,
|
|
2189
2436
|
stickyIndices: [ 0, 1 ],
|
|
2190
2437
|
width: 100,
|
|
@@ -2206,12 +2453,11 @@ describe('virtual-scroll-logic', () => {
|
|
|
2206
2453
|
isSticky: true,
|
|
2207
2454
|
originalX: 0,
|
|
2208
2455
|
originalY: 0,
|
|
2209
|
-
relativeScrollX: 600,
|
|
2456
|
+
relativeScrollX: 600,
|
|
2210
2457
|
relativeScrollY: 0,
|
|
2211
2458
|
stickyIndices: [ 0, 10 ],
|
|
2212
2459
|
width: 50,
|
|
2213
2460
|
});
|
|
2214
|
-
|
|
2215
2461
|
expect(result.isStickyActive).toBe(false);
|
|
2216
2462
|
});
|
|
2217
2463
|
|
|
@@ -2229,7 +2475,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2229
2475
|
isSticky: true,
|
|
2230
2476
|
originalX: 0,
|
|
2231
2477
|
originalY: 0,
|
|
2232
|
-
relativeScrollX: 600,
|
|
2478
|
+
relativeScrollX: 600,
|
|
2233
2479
|
relativeScrollY: 0,
|
|
2234
2480
|
stickyIndices: [ 0, 10 ],
|
|
2235
2481
|
width: 50,
|
|
@@ -2285,7 +2531,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2285
2531
|
|
|
2286
2532
|
it('ensures only one sticky item is active at a time in a sequence', () => {
|
|
2287
2533
|
const stickyIndices = [ 0, 1, 2, 3, 4 ];
|
|
2288
|
-
const scrollY = 75;
|
|
2534
|
+
const scrollY = 75;
|
|
2289
2535
|
|
|
2290
2536
|
const results = stickyIndices.map((idx) => calculateStickyItem({
|
|
2291
2537
|
columnGap: 0,
|
|
@@ -2333,12 +2579,13 @@ describe('virtual-scroll-logic', () => {
|
|
|
2333
2579
|
});
|
|
2334
2580
|
});
|
|
2335
2581
|
|
|
2336
|
-
describe('
|
|
2582
|
+
describe('calculate item style', () => {
|
|
2337
2583
|
it('calculates style for table container', () => {
|
|
2338
2584
|
const result = calculateItemStyle({
|
|
2339
2585
|
containerTag: 'table',
|
|
2340
2586
|
direction: 'vertical',
|
|
2341
2587
|
isHydrated: true,
|
|
2588
|
+
isRtl: false,
|
|
2342
2589
|
item: {
|
|
2343
2590
|
index: 10,
|
|
2344
2591
|
isStickyActive: false,
|
|
@@ -2358,6 +2605,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2358
2605
|
containerTag: 'div',
|
|
2359
2606
|
direction: 'vertical',
|
|
2360
2607
|
isHydrated: true,
|
|
2608
|
+
isRtl: false,
|
|
2361
2609
|
item: {
|
|
2362
2610
|
index: 10,
|
|
2363
2611
|
isStickyActive: false,
|
|
@@ -2378,6 +2626,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2378
2626
|
containerTag: 'div',
|
|
2379
2627
|
direction: 'horizontal',
|
|
2380
2628
|
isHydrated: true,
|
|
2629
|
+
isRtl: false,
|
|
2381
2630
|
item: {
|
|
2382
2631
|
index: 10,
|
|
2383
2632
|
isStickyActive: false,
|
|
@@ -2398,6 +2647,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2398
2647
|
containerTag: 'div',
|
|
2399
2648
|
direction: 'vertical',
|
|
2400
2649
|
isHydrated: true,
|
|
2650
|
+
isRtl: false,
|
|
2401
2651
|
item: {
|
|
2402
2652
|
index: 10,
|
|
2403
2653
|
isStickyActive: true,
|
|
@@ -2410,7 +2660,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2410
2660
|
paddingStartY: 10,
|
|
2411
2661
|
});
|
|
2412
2662
|
expect(result.insetBlockStart).toBe('10px');
|
|
2413
|
-
expect(result.insetInlineStart).
|
|
2663
|
+
expect(result.insetInlineStart).toBe('auto');
|
|
2414
2664
|
});
|
|
2415
2665
|
|
|
2416
2666
|
it('calculates style for sticky item (grid both directions)', () => {
|
|
@@ -2418,9 +2668,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
2418
2668
|
containerTag: 'div',
|
|
2419
2669
|
direction: 'both',
|
|
2420
2670
|
isHydrated: true,
|
|
2671
|
+
isRtl: false,
|
|
2421
2672
|
item: {
|
|
2422
2673
|
index: 10,
|
|
2423
2674
|
isStickyActive: true,
|
|
2675
|
+
isStickyActiveX: true,
|
|
2676
|
+
isStickyActiveY: true,
|
|
2424
2677
|
offset: { x: 600, y: 600 },
|
|
2425
2678
|
size: { height: 50, width: 50 },
|
|
2426
2679
|
stickyOffset: { x: -10, y: -10 },
|
|
@@ -2438,9 +2691,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
2438
2691
|
containerTag: 'div',
|
|
2439
2692
|
direction: 'both',
|
|
2440
2693
|
isHydrated: true,
|
|
2694
|
+
isRtl: false,
|
|
2441
2695
|
item: {
|
|
2442
2696
|
index: 10,
|
|
2443
2697
|
isStickyActive: true,
|
|
2698
|
+
isStickyActiveX: true,
|
|
2699
|
+
isStickyActiveY: true,
|
|
2444
2700
|
offset: { x: 600, y: 600 },
|
|
2445
2701
|
size: { height: 50, width: 50 },
|
|
2446
2702
|
stickyOffset: { x: -10, y: -10 },
|
|
@@ -2453,12 +2709,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
2453
2709
|
expect(result.insetInlineStart).toBe('10px');
|
|
2454
2710
|
expect(result.transform).toBe('translate(-10px, -10px)');
|
|
2455
2711
|
});
|
|
2456
|
-
|
|
2457
2712
|
it('calculates style for non-hydrated item', () => {
|
|
2458
2713
|
const result = calculateItemStyle({
|
|
2459
2714
|
containerTag: 'div',
|
|
2460
2715
|
direction: 'vertical',
|
|
2461
2716
|
isHydrated: false,
|
|
2717
|
+
isRtl: false,
|
|
2462
2718
|
item: {
|
|
2463
2719
|
index: 10,
|
|
2464
2720
|
isStickyActive: false,
|
|
@@ -2478,6 +2734,7 @@ describe('virtual-scroll-logic', () => {
|
|
|
2478
2734
|
containerTag: 'div',
|
|
2479
2735
|
direction: 'horizontal',
|
|
2480
2736
|
isHydrated: true,
|
|
2737
|
+
isRtl: false,
|
|
2481
2738
|
item: {
|
|
2482
2739
|
index: 10,
|
|
2483
2740
|
isStickyActive: true,
|
|
@@ -2498,9 +2755,12 @@ describe('virtual-scroll-logic', () => {
|
|
|
2498
2755
|
containerTag: 'div',
|
|
2499
2756
|
direction: 'both',
|
|
2500
2757
|
isHydrated: true,
|
|
2758
|
+
isRtl: false,
|
|
2501
2759
|
item: {
|
|
2502
2760
|
index: 10,
|
|
2503
2761
|
isStickyActive: true,
|
|
2762
|
+
isStickyActiveX: true,
|
|
2763
|
+
isStickyActiveY: true,
|
|
2504
2764
|
offset: { x: 600, y: 600 },
|
|
2505
2765
|
size: { height: 50, width: 50 },
|
|
2506
2766
|
stickyOffset: { x: -10, y: -20 },
|
|
@@ -2513,5 +2773,95 @@ describe('virtual-scroll-logic', () => {
|
|
|
2513
2773
|
expect(result.insetInlineStart).toBe('10px');
|
|
2514
2774
|
expect(result.transform).toBe('translate(-10px, -20px)');
|
|
2515
2775
|
});
|
|
2776
|
+
it('correctly inverts transform in rtl mode', () => {
|
|
2777
|
+
const item: RenderedItem = {
|
|
2778
|
+
index: 0,
|
|
2779
|
+
item: {},
|
|
2780
|
+
offset: { x: 100, y: 200 },
|
|
2781
|
+
originalX: 100,
|
|
2782
|
+
originalY: 200,
|
|
2783
|
+
size: { height: 50, width: 100 },
|
|
2784
|
+
stickyOffset: { x: 10, y: 20 },
|
|
2785
|
+
};
|
|
2786
|
+
|
|
2787
|
+
// LTR
|
|
2788
|
+
let result = calculateItemStyle({
|
|
2789
|
+
containerTag: 'div',
|
|
2790
|
+
direction: 'vertical',
|
|
2791
|
+
isHydrated: true,
|
|
2792
|
+
isRtl: false,
|
|
2793
|
+
item,
|
|
2794
|
+
itemSize: 50,
|
|
2795
|
+
paddingStartX: 0,
|
|
2796
|
+
paddingStartY: 0,
|
|
2797
|
+
});
|
|
2798
|
+
expect(result.transform).toBe('translate(100px, 200px)');
|
|
2799
|
+
|
|
2800
|
+
// RTL
|
|
2801
|
+
result = calculateItemStyle({
|
|
2802
|
+
containerTag: 'div',
|
|
2803
|
+
direction: 'vertical',
|
|
2804
|
+
isHydrated: true,
|
|
2805
|
+
isRtl: true,
|
|
2806
|
+
item,
|
|
2807
|
+
itemSize: 50,
|
|
2808
|
+
paddingStartX: 0,
|
|
2809
|
+
paddingStartY: 0,
|
|
2810
|
+
});
|
|
2811
|
+
expect(result.transform).toBe('translate(-100px, 200px)');
|
|
2812
|
+
|
|
2813
|
+
// RTL sticky
|
|
2814
|
+
result = calculateItemStyle({
|
|
2815
|
+
containerTag: 'div',
|
|
2816
|
+
direction: 'vertical',
|
|
2817
|
+
isHydrated: true,
|
|
2818
|
+
isRtl: true,
|
|
2819
|
+
item: { ...item, isStickyActive: true },
|
|
2820
|
+
itemSize: 50,
|
|
2821
|
+
paddingStartX: 0,
|
|
2822
|
+
paddingStartY: 0,
|
|
2823
|
+
});
|
|
2824
|
+
expect(result.transform).toBe('translate(-100px, 20px)');
|
|
2825
|
+
|
|
2826
|
+
result = calculateItemStyle({
|
|
2827
|
+
containerTag: 'div',
|
|
2828
|
+
direction: 'horizontal',
|
|
2829
|
+
isHydrated: true,
|
|
2830
|
+
isRtl: true,
|
|
2831
|
+
item: { ...item, isStickyActive: true },
|
|
2832
|
+
itemSize: 50,
|
|
2833
|
+
paddingStartX: 0,
|
|
2834
|
+
paddingStartY: 0,
|
|
2835
|
+
});
|
|
2836
|
+
|
|
2837
|
+
expect(result.transform).toBe('translate(-10px, 200px)');
|
|
2838
|
+
});
|
|
2839
|
+
|
|
2840
|
+
it('maintains 1:1 movement even when scale is high', () => {
|
|
2841
|
+
const item: RenderedItem<unknown> = {
|
|
2842
|
+
index: 100,
|
|
2843
|
+
isSticky: false,
|
|
2844
|
+
isStickyActive: false,
|
|
2845
|
+
item: {},
|
|
2846
|
+
offset: { x: 0, y: 600 },
|
|
2847
|
+
originalX: 0,
|
|
2848
|
+
originalY: 5100,
|
|
2849
|
+
size: { height: 50, width: 100 },
|
|
2850
|
+
stickyOffset: { x: 0, y: 0 },
|
|
2851
|
+
};
|
|
2852
|
+
|
|
2853
|
+
const style = calculateItemStyle({
|
|
2854
|
+
containerTag: 'div',
|
|
2855
|
+
direction: 'vertical',
|
|
2856
|
+
isHydrated: true,
|
|
2857
|
+
isRtl: false,
|
|
2858
|
+
item,
|
|
2859
|
+
itemSize: 50,
|
|
2860
|
+
paddingStartX: 0,
|
|
2861
|
+
paddingStartY: 0,
|
|
2862
|
+
});
|
|
2863
|
+
|
|
2864
|
+
expect(style.transform).toBe('translate(0px, 600px)');
|
|
2865
|
+
});
|
|
2516
2866
|
});
|
|
2517
2867
|
});
|