react-window 1.8.2 → 1.8.6
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/CHANGELOG.md +16 -0
- package/README.md +13 -0
- package/dist/index-dev.umd.js +2 -0
- package/dist/index-dev.umd.js.map +1 -0
- package/dist/index-prod.umd.js +2 -0
- package/dist/index-prod.umd.js.map +1 -0
- package/dist/index.cjs.js +155 -63
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.esm.js +155 -63
- package/dist/index.esm.js.map +1 -0
- package/package.json +12 -9
- package/src/FixedSizeGrid.js +55 -21
- package/src/FixedSizeList.js +27 -14
- package/src/VariableSizeGrid.js +5 -1
- package/src/VariableSizeList.js +1 -1
- package/src/createGridComponent.js +49 -24
- package/src/createListComponent.js +42 -16
- package/src/domHelpers.js +17 -4
package/src/FixedSizeList.js
CHANGED
|
@@ -5,10 +5,10 @@ import createListComponent from './createListComponent';
|
|
|
5
5
|
import type { Props, ScrollToAlign } from './createListComponent';
|
|
6
6
|
|
|
7
7
|
const FixedSizeList = createListComponent({
|
|
8
|
-
getItemOffset: ({ itemSize
|
|
8
|
+
getItemOffset: ({ itemSize }: Props<any>, index: number): number =>
|
|
9
9
|
index * ((itemSize: any): number),
|
|
10
10
|
|
|
11
|
-
getItemSize: ({ itemSize
|
|
11
|
+
getItemSize: ({ itemSize }: Props<any>, index: number): number =>
|
|
12
12
|
((itemSize: any): number),
|
|
13
13
|
|
|
14
14
|
getEstimatedTotalSize: ({ itemCount, itemSize }: Props<any>) =>
|
|
@@ -23,12 +23,13 @@ const FixedSizeList = createListComponent({
|
|
|
23
23
|
// TODO Deprecate direction "horizontal"
|
|
24
24
|
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
|
|
25
25
|
const size = (((isHorizontal ? width : height): any): number);
|
|
26
|
-
const
|
|
26
|
+
const lastItemOffset = Math.max(
|
|
27
27
|
0,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
itemCount * ((itemSize: any): number) - size
|
|
29
|
+
);
|
|
30
|
+
const maxOffset = Math.min(
|
|
31
|
+
lastItemOffset,
|
|
32
|
+
index * ((itemSize: any): number)
|
|
32
33
|
);
|
|
33
34
|
const minOffset = Math.max(
|
|
34
35
|
0,
|
|
@@ -51,13 +52,25 @@ const FixedSizeList = createListComponent({
|
|
|
51
52
|
return maxOffset;
|
|
52
53
|
case 'end':
|
|
53
54
|
return minOffset;
|
|
54
|
-
case 'center':
|
|
55
|
-
|
|
55
|
+
case 'center': {
|
|
56
|
+
// "Centered" offset is usually the average of the min and max.
|
|
57
|
+
// But near the edges of the list, this doesn't hold true.
|
|
58
|
+
const middleOffset = Math.round(
|
|
59
|
+
minOffset + (maxOffset - minOffset) / 2
|
|
60
|
+
);
|
|
61
|
+
if (middleOffset < Math.ceil(size / 2)) {
|
|
62
|
+
return 0; // near the beginning
|
|
63
|
+
} else if (middleOffset > lastItemOffset + Math.floor(size / 2)) {
|
|
64
|
+
return lastItemOffset; // near the end
|
|
65
|
+
} else {
|
|
66
|
+
return middleOffset;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
56
69
|
case 'auto':
|
|
57
70
|
default:
|
|
58
71
|
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
|
59
72
|
return scrollOffset;
|
|
60
|
-
} else if (scrollOffset
|
|
73
|
+
} else if (scrollOffset < minOffset) {
|
|
61
74
|
return minOffset;
|
|
62
75
|
} else {
|
|
63
76
|
return maxOffset;
|
|
@@ -83,14 +96,14 @@ const FixedSizeList = createListComponent({
|
|
|
83
96
|
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
|
|
84
97
|
const offset = startIndex * ((itemSize: any): number);
|
|
85
98
|
const size = (((isHorizontal ? width : height): any): number);
|
|
99
|
+
const numVisibleItems = Math.ceil(
|
|
100
|
+
(size + scrollOffset - offset) / ((itemSize: any): number)
|
|
101
|
+
);
|
|
86
102
|
return Math.max(
|
|
87
103
|
0,
|
|
88
104
|
Math.min(
|
|
89
105
|
itemCount - 1,
|
|
90
|
-
startIndex +
|
|
91
|
-
Math.floor(
|
|
92
|
-
(size + (scrollOffset - offset)) / ((itemSize: any): number)
|
|
93
|
-
)
|
|
106
|
+
startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive
|
|
94
107
|
)
|
|
95
108
|
);
|
|
96
109
|
},
|
package/src/VariableSizeGrid.js
CHANGED
|
@@ -274,7 +274,11 @@ const getOffsetForIndexAndAlignment = (
|
|
|
274
274
|
default:
|
|
275
275
|
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
|
276
276
|
return scrollOffset;
|
|
277
|
-
} else if (
|
|
277
|
+
} else if (minOffset > maxOffset) {
|
|
278
|
+
// Because we only take into account the scrollbar size when calculating minOffset
|
|
279
|
+
// this value can be larger than maxOffset when at the end of the list
|
|
280
|
+
return minOffset;
|
|
281
|
+
} else if (scrollOffset < minOffset) {
|
|
278
282
|
return minOffset;
|
|
279
283
|
} else {
|
|
280
284
|
return maxOffset;
|
package/src/VariableSizeList.js
CHANGED
|
@@ -227,7 +227,7 @@ const VariableSizeList = createListComponent({
|
|
|
227
227
|
default:
|
|
228
228
|
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
|
229
229
|
return scrollOffset;
|
|
230
|
-
} else if (scrollOffset
|
|
230
|
+
} else if (scrollOffset < minOffset) {
|
|
231
231
|
return minOffset;
|
|
232
232
|
} else {
|
|
233
233
|
return maxOffset;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import memoizeOne from 'memoize-one';
|
|
4
4
|
import { createElement, PureComponent } from 'react';
|
|
5
5
|
import { cancelTimeout, requestTimeout } from './timer';
|
|
6
|
-
import { getScrollbarSize,
|
|
6
|
+
import { getScrollbarSize, getRTLOffsetType } from './domHelpers';
|
|
7
7
|
|
|
8
8
|
import type { TimeoutID } from './timer';
|
|
9
9
|
|
|
@@ -46,6 +46,22 @@ type OnScrollCallback = ({
|
|
|
46
46
|
type ScrollEvent = SyntheticEvent<HTMLDivElement>;
|
|
47
47
|
type ItemStyleCache = { [key: string]: Object };
|
|
48
48
|
|
|
49
|
+
type OuterProps = {|
|
|
50
|
+
children: React$Node,
|
|
51
|
+
className: string | void,
|
|
52
|
+
onScroll: ScrollEvent => void,
|
|
53
|
+
style: {
|
|
54
|
+
[string]: mixed,
|
|
55
|
+
},
|
|
56
|
+
|};
|
|
57
|
+
|
|
58
|
+
type InnerProps = {|
|
|
59
|
+
children: React$Node,
|
|
60
|
+
style: {
|
|
61
|
+
[string]: mixed,
|
|
62
|
+
},
|
|
63
|
+
|};
|
|
64
|
+
|
|
49
65
|
export type Props<T> = {|
|
|
50
66
|
children: RenderComponent<T>,
|
|
51
67
|
className?: string,
|
|
@@ -56,7 +72,7 @@ export type Props<T> = {|
|
|
|
56
72
|
initialScrollLeft?: number,
|
|
57
73
|
initialScrollTop?: number,
|
|
58
74
|
innerRef?: any,
|
|
59
|
-
innerElementType?: React$
|
|
75
|
+
innerElementType?: string | React$AbstractComponent<InnerProps, any>,
|
|
60
76
|
innerTagName?: string, // deprecated
|
|
61
77
|
itemData: T,
|
|
62
78
|
itemKey?: (params: {|
|
|
@@ -67,7 +83,7 @@ export type Props<T> = {|
|
|
|
67
83
|
onItemsRendered?: OnItemsRenderedCallback,
|
|
68
84
|
onScroll?: OnScrollCallback,
|
|
69
85
|
outerRef?: any,
|
|
70
|
-
outerElementType?: React$
|
|
86
|
+
outerElementType?: string | React$AbstractComponent<OuterProps, any>,
|
|
71
87
|
outerTagName?: string, // deprecated
|
|
72
88
|
overscanColumnCount?: number,
|
|
73
89
|
overscanColumnsCount?: number, // deprecated
|
|
@@ -348,12 +364,17 @@ export default function createGridComponent({
|
|
|
348
364
|
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
|
349
365
|
const outerRef = ((this._outerRef: any): HTMLElement);
|
|
350
366
|
if (direction === 'rtl') {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
367
|
+
switch (getRTLOffsetType()) {
|
|
368
|
+
case 'negative':
|
|
369
|
+
outerRef.scrollLeft = -scrollLeft;
|
|
370
|
+
break;
|
|
371
|
+
case 'positive-ascending':
|
|
372
|
+
outerRef.scrollLeft = scrollLeft;
|
|
373
|
+
break;
|
|
374
|
+
default:
|
|
375
|
+
const { clientWidth, scrollWidth } = outerRef;
|
|
376
|
+
outerRef.scrollLeft = scrollWidth - clientWidth - scrollLeft;
|
|
377
|
+
break;
|
|
357
378
|
}
|
|
358
379
|
} else {
|
|
359
380
|
outerRef.scrollLeft = Math.max(0, scrollLeft);
|
|
@@ -589,13 +610,16 @@ export default function createGridComponent({
|
|
|
589
610
|
if (itemStyleCache.hasOwnProperty(key)) {
|
|
590
611
|
style = itemStyleCache[key];
|
|
591
612
|
} else {
|
|
613
|
+
const offset = getColumnOffset(
|
|
614
|
+
this.props,
|
|
615
|
+
columnIndex,
|
|
616
|
+
this._instanceProps
|
|
617
|
+
);
|
|
618
|
+
const isRtl = direction === 'rtl';
|
|
592
619
|
itemStyleCache[key] = style = {
|
|
593
620
|
position: 'absolute',
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
columnIndex,
|
|
597
|
-
this._instanceProps
|
|
598
|
-
),
|
|
621
|
+
left: isRtl ? undefined : offset,
|
|
622
|
+
right: isRtl ? offset : undefined,
|
|
599
623
|
top: getRowOffset(this.props, rowIndex, this._instanceProps),
|
|
600
624
|
height: getRowHeight(this.props, rowIndex, this._instanceProps),
|
|
601
625
|
width: getColumnWidth(this.props, columnIndex, this._instanceProps),
|
|
@@ -726,18 +750,19 @@ export default function createGridComponent({
|
|
|
726
750
|
|
|
727
751
|
const { direction } = this.props;
|
|
728
752
|
|
|
753
|
+
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
754
|
+
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
|
755
|
+
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
|
756
|
+
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
|
729
757
|
let calculatedScrollLeft = scrollLeft;
|
|
730
758
|
if (direction === 'rtl') {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
calculatedScrollLeft = -scrollLeft;
|
|
739
|
-
} else {
|
|
740
|
-
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
|
|
759
|
+
switch (getRTLOffsetType()) {
|
|
760
|
+
case 'negative':
|
|
761
|
+
calculatedScrollLeft = -scrollLeft;
|
|
762
|
+
break;
|
|
763
|
+
case 'positive-descending':
|
|
764
|
+
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
|
|
765
|
+
break;
|
|
741
766
|
}
|
|
742
767
|
}
|
|
743
768
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import memoizeOne from 'memoize-one';
|
|
4
4
|
import { createElement, PureComponent } from 'react';
|
|
5
5
|
import { cancelTimeout, requestTimeout } from './timer';
|
|
6
|
-
import {
|
|
6
|
+
import { getRTLOffsetType } from './domHelpers';
|
|
7
7
|
|
|
8
8
|
import type { TimeoutID } from './timer';
|
|
9
9
|
|
|
@@ -39,6 +39,22 @@ type onScrollCallback = ({
|
|
|
39
39
|
type ScrollEvent = SyntheticEvent<HTMLDivElement>;
|
|
40
40
|
type ItemStyleCache = { [index: number]: Object };
|
|
41
41
|
|
|
42
|
+
type OuterProps = {|
|
|
43
|
+
children: React$Node,
|
|
44
|
+
className: string | void,
|
|
45
|
+
onScroll: ScrollEvent => void,
|
|
46
|
+
style: {
|
|
47
|
+
[string]: mixed,
|
|
48
|
+
},
|
|
49
|
+
|};
|
|
50
|
+
|
|
51
|
+
type InnerProps = {|
|
|
52
|
+
children: React$Node,
|
|
53
|
+
style: {
|
|
54
|
+
[string]: mixed,
|
|
55
|
+
},
|
|
56
|
+
|};
|
|
57
|
+
|
|
42
58
|
export type Props<T> = {|
|
|
43
59
|
children: RenderComponent<T>,
|
|
44
60
|
className?: string,
|
|
@@ -46,7 +62,7 @@ export type Props<T> = {|
|
|
|
46
62
|
height: number | string,
|
|
47
63
|
initialScrollOffset?: number,
|
|
48
64
|
innerRef?: any,
|
|
49
|
-
innerElementType?: React$
|
|
65
|
+
innerElementType?: string | React$AbstractComponent<InnerProps, any>,
|
|
50
66
|
innerTagName?: string, // deprecated
|
|
51
67
|
itemCount: number,
|
|
52
68
|
itemData: T,
|
|
@@ -56,7 +72,7 @@ export type Props<T> = {|
|
|
|
56
72
|
onItemsRendered?: onItemsRenderedCallback,
|
|
57
73
|
onScroll?: onScrollCallback,
|
|
58
74
|
outerRef?: any,
|
|
59
|
-
outerElementType?: React$
|
|
75
|
+
outerElementType?: string | React$AbstractComponent<OuterProps, any>,
|
|
60
76
|
outerTagName?: string, // deprecated
|
|
61
77
|
overscanCount: number,
|
|
62
78
|
style?: Object,
|
|
@@ -235,18 +251,24 @@ export default function createListComponent({
|
|
|
235
251
|
|
|
236
252
|
if (scrollUpdateWasRequested && this._outerRef != null) {
|
|
237
253
|
const outerRef = ((this._outerRef: any): HTMLElement);
|
|
254
|
+
|
|
238
255
|
// TODO Deprecate direction "horizontal"
|
|
239
256
|
if (direction === 'horizontal' || layout === 'horizontal') {
|
|
240
257
|
if (direction === 'rtl') {
|
|
241
258
|
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
242
259
|
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
|
243
260
|
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
switch (getRTLOffsetType()) {
|
|
262
|
+
case 'negative':
|
|
263
|
+
outerRef.scrollLeft = -scrollOffset;
|
|
264
|
+
break;
|
|
265
|
+
case 'positive-ascending':
|
|
266
|
+
outerRef.scrollLeft = scrollOffset;
|
|
267
|
+
break;
|
|
268
|
+
default:
|
|
269
|
+
const { clientWidth, scrollWidth } = outerRef;
|
|
270
|
+
outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset;
|
|
271
|
+
break;
|
|
250
272
|
}
|
|
251
273
|
} else {
|
|
252
274
|
outerRef.scrollLeft = scrollOffset;
|
|
@@ -444,9 +466,12 @@ export default function createListComponent({
|
|
|
444
466
|
const isHorizontal =
|
|
445
467
|
direction === 'horizontal' || layout === 'horizontal';
|
|
446
468
|
|
|
469
|
+
const isRtl = direction === 'rtl';
|
|
470
|
+
const offsetHorizontal = isHorizontal ? offset : 0;
|
|
447
471
|
itemStyleCache[index] = style = {
|
|
448
472
|
position: 'absolute',
|
|
449
|
-
|
|
473
|
+
left: isRtl ? undefined : offsetHorizontal,
|
|
474
|
+
right: isRtl ? offsetHorizontal : undefined,
|
|
450
475
|
top: !isHorizontal ? offset : 0,
|
|
451
476
|
height: !isHorizontal ? size : '100%',
|
|
452
477
|
width: isHorizontal ? size : '100%',
|
|
@@ -512,16 +537,17 @@ export default function createListComponent({
|
|
|
512
537
|
|
|
513
538
|
let scrollOffset = scrollLeft;
|
|
514
539
|
if (direction === 'rtl') {
|
|
515
|
-
const isNegative = isRTLOffsetNegative();
|
|
516
|
-
|
|
517
540
|
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
518
541
|
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
|
519
542
|
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
|
520
543
|
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
544
|
+
switch (getRTLOffsetType()) {
|
|
545
|
+
case 'negative':
|
|
546
|
+
scrollOffset = -scrollLeft;
|
|
547
|
+
break;
|
|
548
|
+
case 'positive-descending':
|
|
549
|
+
scrollOffset = scrollWidth - clientWidth - scrollLeft;
|
|
550
|
+
break;
|
|
525
551
|
}
|
|
526
552
|
}
|
|
527
553
|
|
package/src/domHelpers.js
CHANGED
|
@@ -21,7 +21,12 @@ export function getScrollbarSize(recalculate?: boolean = false): number {
|
|
|
21
21
|
return size;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
export type RTLOffsetType =
|
|
25
|
+
| 'negative'
|
|
26
|
+
| 'positive-descending'
|
|
27
|
+
| 'positive-ascending';
|
|
28
|
+
|
|
29
|
+
let cachedRTLResult: RTLOffsetType | null = null;
|
|
25
30
|
|
|
26
31
|
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
27
32
|
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
|
|
@@ -29,7 +34,7 @@ let cachedRTLResult: boolean | null = null;
|
|
|
29
34
|
// The safest way to check this is to intentionally set a negative offset,
|
|
30
35
|
// and then verify that the subsequent "scroll" event matches the negative offset.
|
|
31
36
|
// If it does not match, then we can assume a non-standard RTL scroll implementation.
|
|
32
|
-
export function
|
|
37
|
+
export function getRTLOffsetType(recalculate?: boolean = false): RTLOffsetType {
|
|
33
38
|
if (cachedRTLResult === null || recalculate) {
|
|
34
39
|
const outerDiv = document.createElement('div');
|
|
35
40
|
const outerStyle = outerDiv.style;
|
|
@@ -47,8 +52,16 @@ export function isRTLOffsetNegative(recalculate?: boolean = false): boolean {
|
|
|
47
52
|
|
|
48
53
|
((document.body: any): HTMLBodyElement).appendChild(outerDiv);
|
|
49
54
|
|
|
50
|
-
outerDiv.scrollLeft
|
|
51
|
-
|
|
55
|
+
if (outerDiv.scrollLeft > 0) {
|
|
56
|
+
cachedRTLResult = 'positive-descending';
|
|
57
|
+
} else {
|
|
58
|
+
outerDiv.scrollLeft = 1;
|
|
59
|
+
if (outerDiv.scrollLeft === 0) {
|
|
60
|
+
cachedRTLResult = 'negative';
|
|
61
|
+
} else {
|
|
62
|
+
cachedRTLResult = 'positive-ascending';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
52
65
|
|
|
53
66
|
((document.body: any): HTMLBodyElement).removeChild(outerDiv);
|
|
54
67
|
|