@pdanpdan/virtual-scroll 0.3.0 → 0.5.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 +268 -275
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1497 -192
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2219 -896
- package/dist/index.mjs.map +1 -1
- package/dist/virtual-scroll.css +1 -2
- package/package.json +5 -1
- package/src/components/VirtualScroll.test.ts +1979 -627
- package/src/components/VirtualScroll.vue +951 -349
- package/src/components/VirtualScrollbar.test.ts +174 -0
- package/src/components/VirtualScrollbar.vue +102 -0
- package/src/composables/useVirtualScroll.test.ts +1160 -1521
- package/src/composables/useVirtualScroll.ts +1135 -791
- package/src/composables/useVirtualScrollbar.test.ts +526 -0
- package/src/composables/useVirtualScrollbar.ts +239 -0
- package/src/index.ts +4 -0
- package/src/types.ts +816 -0
- package/src/utils/fenwick-tree.test.ts +39 -39
- package/src/utils/fenwick-tree.ts +38 -18
- package/src/utils/scroll.test.ts +174 -0
- package/src/utils/scroll.ts +50 -13
- package/src/utils/virtual-scroll-logic.test.ts +2850 -0
- package/src/utils/virtual-scroll-logic.ts +901 -0
|
@@ -4,15 +4,15 @@ import { FenwickTree } from './fenwick-tree';
|
|
|
4
4
|
|
|
5
5
|
describe('fenwickTree', () => {
|
|
6
6
|
describe('initialization', () => {
|
|
7
|
-
it('
|
|
7
|
+
it('initializes with correct size', () => {
|
|
8
8
|
const tree = new FenwickTree(5);
|
|
9
9
|
expect(tree.query(5)).toBe(0);
|
|
10
10
|
expect(tree.length).toBe(5);
|
|
11
11
|
});
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
describe('
|
|
15
|
-
it('
|
|
14
|
+
describe('updates & queries', () => {
|
|
15
|
+
it('updates and queries values', () => {
|
|
16
16
|
const tree = new FenwickTree(5);
|
|
17
17
|
tree.update(0, 10);
|
|
18
18
|
tree.update(1, 20);
|
|
@@ -24,7 +24,7 @@ describe('fenwickTree', () => {
|
|
|
24
24
|
expect(tree.query(3)).toBe(60);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
it('
|
|
27
|
+
it('handles updates to existing indices', () => {
|
|
28
28
|
const tree = new FenwickTree(3);
|
|
29
29
|
tree.update(1, 10);
|
|
30
30
|
expect(tree.query(2)).toBe(10);
|
|
@@ -32,7 +32,7 @@ describe('fenwickTree', () => {
|
|
|
32
32
|
expect(tree.query(2)).toBe(15);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
it('
|
|
35
|
+
it('ignores updates for out of bounds indices', () => {
|
|
36
36
|
const tree = new FenwickTree(5);
|
|
37
37
|
tree.update(-1, 10);
|
|
38
38
|
tree.update(5, 10);
|
|
@@ -40,22 +40,29 @@ describe('fenwickTree', () => {
|
|
|
40
40
|
});
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
describe('
|
|
44
|
-
it('
|
|
45
|
-
const tree = new FenwickTree(
|
|
46
|
-
tree.update(0, 10);
|
|
47
|
-
tree.
|
|
48
|
-
tree.
|
|
43
|
+
describe('value access', () => {
|
|
44
|
+
it('returns the individual value at an index', () => {
|
|
45
|
+
const tree = new FenwickTree(3);
|
|
46
|
+
tree.update(0, 10);
|
|
47
|
+
expect(tree.get(0)).toBe(10);
|
|
48
|
+
expect(tree.get(-1)).toBe(0);
|
|
49
|
+
expect(tree.get(10)).toBe(0);
|
|
50
|
+
});
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
it('returns the underlying values array', () => {
|
|
53
|
+
const tree = new FenwickTree(3);
|
|
54
|
+
tree.update(0, 10);
|
|
55
|
+
tree.update(1, 20);
|
|
56
|
+
const values = tree.getValues();
|
|
57
|
+
expect(values).toBeInstanceOf(Float64Array);
|
|
58
|
+
expect(values[ 0 ]).toBe(10);
|
|
59
|
+
expect(values[ 1 ]).toBe(20);
|
|
60
|
+
expect(values[ 2 ]).toBe(0);
|
|
54
61
|
});
|
|
55
62
|
});
|
|
56
63
|
|
|
57
|
-
describe('rebuild
|
|
58
|
-
it('
|
|
64
|
+
describe('rebuild & resize', () => {
|
|
65
|
+
it('sets and rebuilds correctly', () => {
|
|
59
66
|
const tree = new FenwickTree(5);
|
|
60
67
|
tree.set(0, 10);
|
|
61
68
|
tree.set(1, 20);
|
|
@@ -67,7 +74,7 @@ describe('fenwickTree', () => {
|
|
|
67
74
|
expect(tree.query(3)).toBe(60);
|
|
68
75
|
});
|
|
69
76
|
|
|
70
|
-
it('
|
|
77
|
+
it('resizes and preserves existing values', () => {
|
|
71
78
|
const tree = new FenwickTree(5);
|
|
72
79
|
tree.update(0, 10);
|
|
73
80
|
tree.resize(10);
|
|
@@ -77,36 +84,29 @@ describe('fenwickTree', () => {
|
|
|
77
84
|
});
|
|
78
85
|
});
|
|
79
86
|
|
|
80
|
-
describe('
|
|
81
|
-
it('
|
|
82
|
-
const tree = new FenwickTree(
|
|
83
|
-
tree.update(0, 10);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
expect(tree.get(10)).toBe(0);
|
|
87
|
-
});
|
|
87
|
+
describe('search & bounds', () => {
|
|
88
|
+
it('finds lower bound correctly', () => {
|
|
89
|
+
const tree = new FenwickTree(5);
|
|
90
|
+
tree.update(0, 10); // sum up to 1: 10
|
|
91
|
+
tree.update(1, 10); // sum up to 2: 20
|
|
92
|
+
tree.update(2, 10); // sum up to 3: 30
|
|
88
93
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
tree.
|
|
92
|
-
tree.
|
|
93
|
-
const values = tree.getValues();
|
|
94
|
-
expect(values).toBeInstanceOf(Float64Array);
|
|
95
|
-
expect(values[ 0 ]).toBe(10);
|
|
96
|
-
expect(values[ 1 ]).toBe(20);
|
|
97
|
-
expect(values[ 2 ]).toBe(0);
|
|
94
|
+
expect(tree.findLowerBound(5)).toBe(0);
|
|
95
|
+
expect(tree.findLowerBound(15)).toBe(1);
|
|
96
|
+
expect(tree.findLowerBound(25)).toBe(2);
|
|
97
|
+
expect(tree.findLowerBound(35)).toBe(5); // Returns size when not found
|
|
98
98
|
});
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
-
describe('shift', () => {
|
|
102
|
-
it('
|
|
101
|
+
describe('shift operations', () => {
|
|
102
|
+
it('does nothing when offset is 0', () => {
|
|
103
103
|
const tree = new FenwickTree(3);
|
|
104
104
|
tree.update(0, 10);
|
|
105
105
|
tree.shift(0);
|
|
106
106
|
expect(tree.get(0)).toBe(10);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
it('
|
|
109
|
+
it('shifts values forward when offset is positive', () => {
|
|
110
110
|
const tree = new FenwickTree(5);
|
|
111
111
|
tree.update(0, 10);
|
|
112
112
|
tree.update(1, 20);
|
|
@@ -119,7 +119,7 @@ describe('fenwickTree', () => {
|
|
|
119
119
|
expect(tree.query(4)).toBe(30);
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
it('
|
|
122
|
+
it('shifts values backward when offset is negative', () => {
|
|
123
123
|
const tree = new FenwickTree(5);
|
|
124
124
|
tree.update(2, 10);
|
|
125
125
|
tree.update(3, 20);
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fenwick Tree (Binary Indexed Tree) implementation for efficient
|
|
3
3
|
* prefix sum calculations and updates.
|
|
4
|
+
*
|
|
5
|
+
* Provides O(log n) time complexity for both point updates and prefix sum queries.
|
|
4
6
|
*/
|
|
5
7
|
export class FenwickTree {
|
|
6
8
|
private tree: Float64Array;
|
|
7
9
|
private values: Float64Array;
|
|
8
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new Fenwick Tree with the specified size.
|
|
13
|
+
*
|
|
14
|
+
* @param size - The number of elements in the tree.
|
|
15
|
+
*/
|
|
9
16
|
constructor(size: number) {
|
|
10
17
|
this.tree = new Float64Array(size + 1);
|
|
11
18
|
this.values = new Float64Array(size);
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
/**
|
|
15
|
-
* Update the value at a specific index and propagate changes.
|
|
16
|
-
*
|
|
17
|
-
* @param
|
|
22
|
+
* Update the value at a specific index and propagate changes throughout the tree.
|
|
23
|
+
*
|
|
24
|
+
* @param index - The 0-based index to update.
|
|
25
|
+
* @param delta - The change in value (new value - old value).
|
|
18
26
|
*/
|
|
19
27
|
update(index: number, delta: number): void {
|
|
20
28
|
if (index < 0 || index >= this.values.length) {
|
|
@@ -31,8 +39,9 @@ export class FenwickTree {
|
|
|
31
39
|
|
|
32
40
|
/**
|
|
33
41
|
* Get the prefix sum up to a specific index (exclusive).
|
|
34
|
-
*
|
|
35
|
-
* @returns
|
|
42
|
+
*
|
|
43
|
+
* @param index - 0-based index. `query(n)` returns sum of values from index 0 to n-1.
|
|
44
|
+
* @returns Sum of values in range [0, index).
|
|
36
45
|
*/
|
|
37
46
|
query(index: number): number {
|
|
38
47
|
let sum = 0;
|
|
@@ -44,8 +53,11 @@ export class FenwickTree {
|
|
|
44
53
|
}
|
|
45
54
|
|
|
46
55
|
/**
|
|
47
|
-
* Set the individual value at an index without updating the tree.
|
|
48
|
-
* Call rebuild() after multiple sets to update the tree efficiently.
|
|
56
|
+
* Set the individual value at an index without updating the prefix sum tree.
|
|
57
|
+
* Call `rebuild()` after multiple sets to update the tree efficiently in O(n).
|
|
58
|
+
*
|
|
59
|
+
* @param index - The 0-based index.
|
|
60
|
+
* @param value - The new value.
|
|
49
61
|
*/
|
|
50
62
|
set(index: number, value: number): void {
|
|
51
63
|
if (index < 0 || index >= this.values.length) {
|
|
@@ -62,14 +74,19 @@ export class FenwickTree {
|
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
/**
|
|
65
|
-
* Get the individual value at
|
|
77
|
+
* Get the individual value at a specific index.
|
|
78
|
+
*
|
|
79
|
+
* @param index - The 0-based index.
|
|
80
|
+
* @returns The value at the specified index.
|
|
66
81
|
*/
|
|
67
82
|
get(index: number): number {
|
|
68
83
|
return this.values[ index ] || 0;
|
|
69
84
|
}
|
|
70
85
|
|
|
71
86
|
/**
|
|
72
|
-
* Get the underlying values array.
|
|
87
|
+
* Get the underlying values array as a read-only Float64Array.
|
|
88
|
+
*
|
|
89
|
+
* @returns The read-only values array.
|
|
73
90
|
*/
|
|
74
91
|
getValues(): Readonly<Float64Array> {
|
|
75
92
|
return this.values;
|
|
@@ -77,9 +94,10 @@ export class FenwickTree {
|
|
|
77
94
|
|
|
78
95
|
/**
|
|
79
96
|
* Find the largest index such that the prefix sum is less than or equal to the given value.
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* @
|
|
97
|
+
* Highly efficient search used to find which item is at a specific scroll offset.
|
|
98
|
+
*
|
|
99
|
+
* @param value - The prefix sum value to search for.
|
|
100
|
+
* @returns The 0-based index.
|
|
83
101
|
*/
|
|
84
102
|
findLowerBound(value: number): number {
|
|
85
103
|
let index = 0;
|
|
@@ -101,8 +119,8 @@ export class FenwickTree {
|
|
|
101
119
|
}
|
|
102
120
|
|
|
103
121
|
/**
|
|
104
|
-
* Rebuild the entire tree from the current values array
|
|
105
|
-
*
|
|
122
|
+
* Rebuild the entire prefix sum tree from the current values array.
|
|
123
|
+
* Time complexity: O(n).
|
|
106
124
|
*/
|
|
107
125
|
rebuild(): void {
|
|
108
126
|
this.tree.fill(0);
|
|
@@ -118,8 +136,9 @@ export class FenwickTree {
|
|
|
118
136
|
}
|
|
119
137
|
|
|
120
138
|
/**
|
|
121
|
-
* Resize the tree while preserving existing values.
|
|
122
|
-
*
|
|
139
|
+
* Resize the tree while preserving existing values and rebuilding the prefix sums.
|
|
140
|
+
*
|
|
141
|
+
* @param size - The new size of the tree.
|
|
123
142
|
*/
|
|
124
143
|
resize(size: number): void {
|
|
125
144
|
if (size === this.values.length) {
|
|
@@ -135,8 +154,9 @@ export class FenwickTree {
|
|
|
135
154
|
|
|
136
155
|
/**
|
|
137
156
|
* Shift values by a given offset and rebuild the tree.
|
|
138
|
-
* Useful when items are prepended to the list.
|
|
139
|
-
*
|
|
157
|
+
* Useful when items are prepended to the list to maintain existing measurements.
|
|
158
|
+
*
|
|
159
|
+
* @param offset - Number of positions to shift. Positive for prepending (shifts right).
|
|
140
160
|
*/
|
|
141
161
|
shift(offset: number): void {
|
|
142
162
|
if (offset === 0) {
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { getPaddingX, getPaddingY, isBody, isElement, isScrollableElement, isScrollToIndexOptions, isWindow, isWindowLike } from './scroll';
|
|
4
|
+
|
|
5
|
+
describe('scroll utils', () => {
|
|
6
|
+
describe('element type guards', () => {
|
|
7
|
+
describe('iswindow', () => {
|
|
8
|
+
it('returns true for null', () => {
|
|
9
|
+
expect(isWindow(null)).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('returns true for window object', () => {
|
|
13
|
+
expect(isWindow(window)).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('returns true for document.documentelement object', () => {
|
|
17
|
+
expect(isWindow(document.documentElement)).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('returns false for an element', () => {
|
|
21
|
+
const el = document.createElement('div');
|
|
22
|
+
expect(isWindow(el)).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('returns false for undefined', () => {
|
|
26
|
+
expect(isWindow(undefined)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('isbody', () => {
|
|
31
|
+
it('returns true for document.body', () => {
|
|
32
|
+
expect(isBody(document.body)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns false for null', () => {
|
|
36
|
+
expect(isBody(null)).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns false for undefined', () => {
|
|
40
|
+
expect(isBody(undefined)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns false for a string', () => {
|
|
44
|
+
// @ts-expect-error testing invalid input
|
|
45
|
+
expect(isBody('not an object')).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('returns false for a plain object', () => {
|
|
49
|
+
// @ts-expect-error testing invalid input
|
|
50
|
+
expect(isBody({})).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns false for a div', () => {
|
|
54
|
+
const el = document.createElement('div');
|
|
55
|
+
expect(isBody(el)).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('returns false for window', () => {
|
|
59
|
+
expect(isBody(window)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('returns false for document.documentelement', () => {
|
|
63
|
+
expect(isBody(document.documentElement)).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('iswindowlike', () => {
|
|
68
|
+
it('returns true for window', () => {
|
|
69
|
+
expect(isWindowLike(window)).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('returns true for document.documentelement', () => {
|
|
73
|
+
expect(isWindowLike(document.documentElement)).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('returns true for body', () => {
|
|
77
|
+
expect(isWindowLike(document.body)).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('returns true for null', () => {
|
|
81
|
+
expect(isWindowLike(null)).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('returns false for a div', () => {
|
|
85
|
+
const el = document.createElement('div');
|
|
86
|
+
expect(isWindowLike(el)).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('iselement', () => {
|
|
91
|
+
it('returns true for a div', () => {
|
|
92
|
+
const el = document.createElement('div');
|
|
93
|
+
expect(isElement(el)).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('returns true for document.documentelement', () => {
|
|
97
|
+
expect(isElement(document.documentElement)).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('returns false for window', () => {
|
|
101
|
+
expect(isElement(window)).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('returns false for null', () => {
|
|
105
|
+
expect(isElement(null)).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('isscrollableelement', () => {
|
|
110
|
+
it('returns true for a div', () => {
|
|
111
|
+
const el = document.createElement('div');
|
|
112
|
+
expect(isScrollableElement(el)).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('returns false for null', () => {
|
|
116
|
+
expect(isScrollableElement(null)).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('options type guards', () => {
|
|
122
|
+
describe('isscrolltoindexoptions', () => {
|
|
123
|
+
it('returns true for valid options', () => {
|
|
124
|
+
expect(isScrollToIndexOptions({ align: 'start' })).toBe(true);
|
|
125
|
+
expect(isScrollToIndexOptions({ behavior: 'smooth' })).toBe(true);
|
|
126
|
+
expect(isScrollToIndexOptions({ isCorrection: true })).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('returns false for other values', () => {
|
|
130
|
+
expect(isScrollToIndexOptions(null)).toBe(false);
|
|
131
|
+
expect(isScrollToIndexOptions('start')).toBe(false);
|
|
132
|
+
expect(isScrollToIndexOptions({})).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('padding utilities', () => {
|
|
138
|
+
describe('getpaddingx', () => {
|
|
139
|
+
it('handles numeric padding', () => {
|
|
140
|
+
expect(getPaddingX(10, 'horizontal')).toBe(10);
|
|
141
|
+
expect(getPaddingX(10, 'both')).toBe(10);
|
|
142
|
+
expect(getPaddingX(10, 'vertical')).toBe(0);
|
|
143
|
+
expect(getPaddingX(0, 'horizontal')).toBe(0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('handles object padding', () => {
|
|
147
|
+
expect(getPaddingX({ x: 15 }, 'vertical')).toBe(15);
|
|
148
|
+
expect(getPaddingX({ y: 20 }, 'horizontal')).toBe(0);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('returns 0 for undefined', () => {
|
|
152
|
+
expect(getPaddingX(undefined)).toBe(0);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('getpaddingy', () => {
|
|
157
|
+
it('handles numeric padding', () => {
|
|
158
|
+
expect(getPaddingY(10, 'vertical')).toBe(10);
|
|
159
|
+
expect(getPaddingY(10, 'both')).toBe(10);
|
|
160
|
+
expect(getPaddingY(10, 'horizontal')).toBe(0);
|
|
161
|
+
expect(getPaddingY(0, 'vertical')).toBe(0);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('handles object padding', () => {
|
|
165
|
+
expect(getPaddingY({ y: 15 }, 'horizontal')).toBe(15);
|
|
166
|
+
expect(getPaddingY({ x: 20 }, 'vertical')).toBe(0);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('returns 0 for undefined', () => {
|
|
170
|
+
expect(getPaddingY(undefined)).toBe(0);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|
package/src/utils/scroll.ts
CHANGED
|
@@ -1,37 +1,74 @@
|
|
|
1
|
-
import type { ScrollDirection, ScrollToIndexOptions } from '../
|
|
1
|
+
import type { ScrollDirection, ScrollToIndexOptions } from '../types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Maximum size (in pixels) for an element that most browsers can handle reliably.
|
|
5
|
+
* Beyond this size, we use scaling for the scrollable area.
|
|
6
|
+
* @default 10000000
|
|
7
|
+
*/
|
|
8
|
+
export const BROWSER_MAX_SIZE = 10000000;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks if the container is the window object.
|
|
12
|
+
*
|
|
13
|
+
* @param container - The container element or window to check.
|
|
14
|
+
* @returns `true` if the container is the global window object.
|
|
15
|
+
*/
|
|
16
|
+
export function isWindow(container: HTMLElement | Window | null | undefined): container is Window {
|
|
17
|
+
return container === null || container === document.documentElement || (typeof window !== 'undefined' && container === window);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Checks if the container is the document body element.
|
|
22
|
+
*
|
|
23
|
+
* @param container - The container element or window to check.
|
|
24
|
+
* @returns `true` if the container is the `<body>` element.
|
|
25
|
+
*/
|
|
26
|
+
export function isBody(container: HTMLElement | Window | null | undefined): container is HTMLElement {
|
|
27
|
+
return container != null && typeof container === 'object' && 'tagName' in container && container.tagName === 'BODY';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Checks if the container is window-like (global window or document body).
|
|
5
32
|
*
|
|
6
33
|
* @param container - The container element or window to check.
|
|
7
|
-
* @returns
|
|
34
|
+
* @returns `true` if the container is window or body.
|
|
35
|
+
*/
|
|
36
|
+
export function isWindowLike(container: HTMLElement | Window | null | undefined): boolean {
|
|
37
|
+
return isWindow(container) || isBody(container);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Checks if the container is a valid HTML Element with bounding rect support.
|
|
42
|
+
*
|
|
43
|
+
* @param container - The container to check.
|
|
44
|
+
* @returns `true` if the container is an `HTMLElement`.
|
|
8
45
|
*/
|
|
9
46
|
export function isElement(container: HTMLElement | Window | null | undefined): container is HTMLElement {
|
|
10
|
-
return
|
|
47
|
+
return container != null && 'getBoundingClientRect' in container;
|
|
11
48
|
}
|
|
12
49
|
|
|
13
50
|
/**
|
|
14
|
-
* Checks if the target is an element
|
|
51
|
+
* Checks if the target is an element that supports scrolling.
|
|
15
52
|
*
|
|
16
53
|
* @param target - The event target to check.
|
|
17
|
-
* @returns
|
|
54
|
+
* @returns `true` if the target is an `HTMLElement` with scroll properties.
|
|
18
55
|
*/
|
|
19
56
|
export function isScrollableElement(target: EventTarget | null): target is HTMLElement {
|
|
20
|
-
return
|
|
57
|
+
return target != null && 'scrollLeft' in target;
|
|
21
58
|
}
|
|
22
59
|
|
|
23
60
|
/**
|
|
24
|
-
* Helper to determine if an options argument is
|
|
61
|
+
* Helper to determine if an options argument is a full `ScrollToIndexOptions` object.
|
|
25
62
|
*
|
|
26
63
|
* @param options - The options object to check.
|
|
27
|
-
* @returns
|
|
64
|
+
* @returns `true` if the options object contains scroll-to-index specific properties.
|
|
28
65
|
*/
|
|
29
66
|
export function isScrollToIndexOptions(options: unknown): options is ScrollToIndexOptions {
|
|
30
|
-
return typeof options === 'object' && options
|
|
67
|
+
return typeof options === 'object' && options != null && ('align' in options || 'behavior' in options || 'isCorrection' in options);
|
|
31
68
|
}
|
|
32
69
|
|
|
33
70
|
/**
|
|
34
|
-
* Extracts the horizontal padding from a padding
|
|
71
|
+
* Extracts the horizontal padding from a padding configuration.
|
|
35
72
|
*
|
|
36
73
|
* @param p - The padding value (number or object with x/y).
|
|
37
74
|
* @param direction - The current scroll direction.
|
|
@@ -41,11 +78,11 @@ export function getPaddingX(p: number | { x?: number; y?: number; } | undefined,
|
|
|
41
78
|
if (typeof p === 'object' && p !== null) {
|
|
42
79
|
return p.x || 0;
|
|
43
80
|
}
|
|
44
|
-
return direction === 'horizontal' ? (p || 0) : 0;
|
|
81
|
+
return (direction === 'horizontal' || direction === 'both') ? (p || 0) : 0;
|
|
45
82
|
}
|
|
46
83
|
|
|
47
84
|
/**
|
|
48
|
-
* Extracts the vertical padding from a padding
|
|
85
|
+
* Extracts the vertical padding from a padding configuration.
|
|
49
86
|
*
|
|
50
87
|
* @param p - The padding value (number or object with x/y).
|
|
51
88
|
* @param direction - The current scroll direction.
|