react-window 1.8.1 → 1.8.5
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 +19 -0
- package/README.md +8 -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 +245 -60
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.esm.js +245 -60
- package/dist/index.esm.js.map +1 -0
- package/package.json +5 -3
- 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 +106 -24
- package/src/createListComponent.js +72 -20
- package/src/domHelpers.js +50 -0
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 } from './domHelpers';
|
|
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,11 +83,13 @@ 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,
|
|
89
|
+
overscanColumnsCount?: number, // deprecated
|
|
73
90
|
overscanCount?: number, // deprecated
|
|
74
|
-
|
|
91
|
+
overscanRowCount?: number,
|
|
92
|
+
overscanRowsCount?: number, // deprecated
|
|
75
93
|
rowCount: number,
|
|
76
94
|
rowHeight: itemSize,
|
|
77
95
|
style?: Object,
|
|
@@ -130,10 +148,12 @@ const defaultItemKey = ({ columnIndex, data, rowIndex }) =>
|
|
|
130
148
|
// In DEV mode, this Set helps us only log a warning once per component instance.
|
|
131
149
|
// This avoids spamming the console every time a render happens.
|
|
132
150
|
let devWarningsOverscanCount = null;
|
|
151
|
+
let devWarningsOverscanRowsColumnsCount = null;
|
|
133
152
|
let devWarningsTagName = null;
|
|
134
153
|
if (process.env.NODE_ENV !== 'production') {
|
|
135
154
|
if (typeof window !== 'undefined' && typeof window.WeakSet !== 'undefined') {
|
|
136
155
|
devWarningsOverscanCount = new WeakSet();
|
|
156
|
+
devWarningsOverscanRowsColumnsCount = new WeakSet();
|
|
137
157
|
devWarningsTagName = new WeakSet();
|
|
138
158
|
}
|
|
139
159
|
}
|
|
@@ -320,21 +340,47 @@ export default function createGridComponent({
|
|
|
320
340
|
|
|
321
341
|
componentDidMount() {
|
|
322
342
|
const { initialScrollLeft, initialScrollTop } = this.props;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
343
|
+
|
|
344
|
+
if (this._outerRef != null) {
|
|
345
|
+
const outerRef = ((this._outerRef: any): HTMLElement);
|
|
346
|
+
if (typeof initialScrollLeft === 'number') {
|
|
347
|
+
outerRef.scrollLeft = initialScrollLeft;
|
|
348
|
+
}
|
|
349
|
+
if (typeof initialScrollTop === 'number') {
|
|
350
|
+
outerRef.scrollTop = initialScrollTop;
|
|
351
|
+
}
|
|
328
352
|
}
|
|
329
353
|
|
|
330
354
|
this._callPropsCallbacks();
|
|
331
355
|
}
|
|
332
356
|
|
|
333
357
|
componentDidUpdate() {
|
|
358
|
+
const { direction } = this.props;
|
|
334
359
|
const { scrollLeft, scrollTop, scrollUpdateWasRequested } = this.state;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
360
|
+
|
|
361
|
+
if (scrollUpdateWasRequested && this._outerRef != null) {
|
|
362
|
+
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
363
|
+
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
|
364
|
+
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
|
365
|
+
const outerRef = ((this._outerRef: any): HTMLElement);
|
|
366
|
+
if (direction === 'rtl') {
|
|
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;
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
outerRef.scrollLeft = Math.max(0, scrollLeft);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
outerRef.scrollTop = Math.max(0, scrollTop);
|
|
338
384
|
}
|
|
339
385
|
|
|
340
386
|
this._callPropsCallbacks();
|
|
@@ -586,6 +632,7 @@ export default function createGridComponent({
|
|
|
586
632
|
_getHorizontalRangeToRender(): [number, number, number, number] {
|
|
587
633
|
const {
|
|
588
634
|
columnCount,
|
|
635
|
+
overscanColumnCount,
|
|
589
636
|
overscanColumnsCount,
|
|
590
637
|
overscanCount,
|
|
591
638
|
rowCount,
|
|
@@ -593,7 +640,7 @@ export default function createGridComponent({
|
|
|
593
640
|
const { horizontalScrollDirection, isScrolling, scrollLeft } = this.state;
|
|
594
641
|
|
|
595
642
|
const overscanCountResolved: number =
|
|
596
|
-
overscanColumnsCount || overscanCount || 1;
|
|
643
|
+
overscanColumnCount || overscanColumnsCount || overscanCount || 1;
|
|
597
644
|
|
|
598
645
|
if (columnCount === 0 || rowCount === 0) {
|
|
599
646
|
return [0, 0, 0, 0];
|
|
@@ -634,13 +681,14 @@ export default function createGridComponent({
|
|
|
634
681
|
const {
|
|
635
682
|
columnCount,
|
|
636
683
|
overscanCount,
|
|
684
|
+
overscanRowCount,
|
|
637
685
|
overscanRowsCount,
|
|
638
686
|
rowCount,
|
|
639
687
|
} = this.props;
|
|
640
688
|
const { isScrolling, verticalScrollDirection, scrollTop } = this.state;
|
|
641
689
|
|
|
642
690
|
const overscanCountResolved: number =
|
|
643
|
-
overscanRowsCount || overscanCount || 1;
|
|
691
|
+
overscanRowCount || overscanRowsCount || overscanCount || 1;
|
|
644
692
|
|
|
645
693
|
if (columnCount === 0 || rowCount === 0) {
|
|
646
694
|
return [0, 0, 0, 0];
|
|
@@ -679,9 +727,11 @@ export default function createGridComponent({
|
|
|
679
727
|
|
|
680
728
|
_onScroll = (event: ScrollEvent): void => {
|
|
681
729
|
const {
|
|
730
|
+
clientHeight,
|
|
682
731
|
clientWidth,
|
|
683
732
|
scrollLeft,
|
|
684
733
|
scrollTop,
|
|
734
|
+
scrollHeight,
|
|
685
735
|
scrollWidth,
|
|
686
736
|
} = event.currentTarget;
|
|
687
737
|
this.setState(prevState => {
|
|
@@ -697,24 +747,38 @@ export default function createGridComponent({
|
|
|
697
747
|
|
|
698
748
|
const { direction } = this.props;
|
|
699
749
|
|
|
700
|
-
//
|
|
701
|
-
//
|
|
702
|
-
//
|
|
750
|
+
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
751
|
+
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
|
752
|
+
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
|
753
|
+
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
|
703
754
|
let calculatedScrollLeft = scrollLeft;
|
|
704
755
|
if (direction === 'rtl') {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
756
|
+
switch (getRTLOffsetType()) {
|
|
757
|
+
case 'negative':
|
|
758
|
+
calculatedScrollLeft = -scrollLeft;
|
|
759
|
+
break;
|
|
760
|
+
case 'positive-descending':
|
|
761
|
+
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
|
|
762
|
+
break;
|
|
709
763
|
}
|
|
710
764
|
}
|
|
711
765
|
|
|
766
|
+
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
|
767
|
+
calculatedScrollLeft = Math.max(
|
|
768
|
+
0,
|
|
769
|
+
Math.min(calculatedScrollLeft, scrollWidth - clientWidth)
|
|
770
|
+
);
|
|
771
|
+
const calculatedScrollTop = Math.max(
|
|
772
|
+
0,
|
|
773
|
+
Math.min(scrollTop, scrollHeight - clientHeight)
|
|
774
|
+
);
|
|
775
|
+
|
|
712
776
|
return {
|
|
713
777
|
isScrolling: true,
|
|
714
778
|
horizontalScrollDirection:
|
|
715
779
|
prevState.scrollLeft < scrollLeft ? 'forward' : 'backward',
|
|
716
780
|
scrollLeft: calculatedScrollLeft,
|
|
717
|
-
scrollTop,
|
|
781
|
+
scrollTop: calculatedScrollTop,
|
|
718
782
|
verticalScrollDirection:
|
|
719
783
|
prevState.scrollTop < scrollTop ? 'forward' : 'backward',
|
|
720
784
|
scrollUpdateWasRequested: false,
|
|
@@ -768,7 +832,9 @@ const validateSharedProps = (
|
|
|
768
832
|
height,
|
|
769
833
|
innerTagName,
|
|
770
834
|
outerTagName,
|
|
835
|
+
overscanColumnsCount,
|
|
771
836
|
overscanCount,
|
|
837
|
+
overscanRowsCount,
|
|
772
838
|
width,
|
|
773
839
|
}: Props<any>,
|
|
774
840
|
{ instance }: State
|
|
@@ -779,7 +845,23 @@ const validateSharedProps = (
|
|
|
779
845
|
devWarningsOverscanCount.add(instance);
|
|
780
846
|
console.warn(
|
|
781
847
|
'The overscanCount prop has been deprecated. ' +
|
|
782
|
-
'Please use the
|
|
848
|
+
'Please use the overscanColumnCount and overscanRowCount props instead.'
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (
|
|
854
|
+
typeof overscanColumnsCount === 'number' ||
|
|
855
|
+
typeof overscanRowsCount === 'number'
|
|
856
|
+
) {
|
|
857
|
+
if (
|
|
858
|
+
devWarningsOverscanRowsColumnsCount &&
|
|
859
|
+
!devWarningsOverscanRowsColumnsCount.has(instance)
|
|
860
|
+
) {
|
|
861
|
+
devWarningsOverscanRowsColumnsCount.add(instance);
|
|
862
|
+
console.warn(
|
|
863
|
+
'The overscanColumnsCount and overscanRowsCount props have been deprecated. ' +
|
|
864
|
+
'Please use the overscanColumnCount and overscanRowCount props instead.'
|
|
783
865
|
);
|
|
784
866
|
}
|
|
785
867
|
}
|
|
@@ -3,6 +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 { getRTLOffsetType } from './domHelpers';
|
|
6
7
|
|
|
7
8
|
import type { TimeoutID } from './timer';
|
|
8
9
|
|
|
@@ -38,6 +39,22 @@ type onScrollCallback = ({
|
|
|
38
39
|
type ScrollEvent = SyntheticEvent<HTMLDivElement>;
|
|
39
40
|
type ItemStyleCache = { [index: number]: Object };
|
|
40
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
|
+
|
|
41
58
|
export type Props<T> = {|
|
|
42
59
|
children: RenderComponent<T>,
|
|
43
60
|
className?: string,
|
|
@@ -45,7 +62,7 @@ export type Props<T> = {|
|
|
|
45
62
|
height: number | string,
|
|
46
63
|
initialScrollOffset?: number,
|
|
47
64
|
innerRef?: any,
|
|
48
|
-
innerElementType?: React$
|
|
65
|
+
innerElementType?: string | React$AbstractComponent<InnerProps, any>,
|
|
49
66
|
innerTagName?: string, // deprecated
|
|
50
67
|
itemCount: number,
|
|
51
68
|
itemData: T,
|
|
@@ -55,7 +72,7 @@ export type Props<T> = {|
|
|
|
55
72
|
onItemsRendered?: onItemsRenderedCallback,
|
|
56
73
|
onScroll?: onScrollCallback,
|
|
57
74
|
outerRef?: any,
|
|
58
|
-
outerElementType?: React$
|
|
75
|
+
outerElementType?: string | React$AbstractComponent<OuterProps, any>,
|
|
59
76
|
outerTagName?: string, // deprecated
|
|
60
77
|
overscanCount: number,
|
|
61
78
|
style?: Object,
|
|
@@ -215,14 +232,13 @@ export default function createListComponent({
|
|
|
215
232
|
componentDidMount() {
|
|
216
233
|
const { direction, initialScrollOffset, layout } = this.props;
|
|
217
234
|
|
|
218
|
-
if (typeof initialScrollOffset === 'number' && this._outerRef
|
|
235
|
+
if (typeof initialScrollOffset === 'number' && this._outerRef != null) {
|
|
236
|
+
const outerRef = ((this._outerRef: any): HTMLElement);
|
|
219
237
|
// TODO Deprecate direction "horizontal"
|
|
220
238
|
if (direction === 'horizontal' || layout === 'horizontal') {
|
|
221
|
-
|
|
222
|
-
._outerRef: any): HTMLDivElement).scrollLeft = initialScrollOffset;
|
|
239
|
+
outerRef.scrollLeft = initialScrollOffset;
|
|
223
240
|
} else {
|
|
224
|
-
|
|
225
|
-
._outerRef: any): HTMLDivElement).scrollTop = initialScrollOffset;
|
|
241
|
+
outerRef.scrollTop = initialScrollOffset;
|
|
226
242
|
}
|
|
227
243
|
}
|
|
228
244
|
|
|
@@ -233,12 +249,32 @@ export default function createListComponent({
|
|
|
233
249
|
const { direction, layout } = this.props;
|
|
234
250
|
const { scrollOffset, scrollUpdateWasRequested } = this.state;
|
|
235
251
|
|
|
236
|
-
if (scrollUpdateWasRequested && this._outerRef
|
|
252
|
+
if (scrollUpdateWasRequested && this._outerRef != null) {
|
|
253
|
+
const outerRef = ((this._outerRef: any): HTMLElement);
|
|
254
|
+
|
|
237
255
|
// TODO Deprecate direction "horizontal"
|
|
238
256
|
if (direction === 'horizontal' || layout === 'horizontal') {
|
|
239
|
-
(
|
|
257
|
+
if (direction === 'rtl') {
|
|
258
|
+
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
259
|
+
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
|
260
|
+
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
|
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;
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
outerRef.scrollLeft = scrollOffset;
|
|
275
|
+
}
|
|
240
276
|
} else {
|
|
241
|
-
|
|
277
|
+
outerRef.scrollTop = scrollOffset;
|
|
242
278
|
}
|
|
243
279
|
}
|
|
244
280
|
|
|
@@ -496,18 +532,28 @@ export default function createListComponent({
|
|
|
496
532
|
|
|
497
533
|
const { direction } = this.props;
|
|
498
534
|
|
|
499
|
-
// HACK According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
500
|
-
// Chrome does not seem to adhere; its scrolLeft values are positive (measured relative to the left).
|
|
501
|
-
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
|
|
502
535
|
let scrollOffset = scrollLeft;
|
|
503
536
|
if (direction === 'rtl') {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
537
|
+
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
538
|
+
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
|
539
|
+
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
|
540
|
+
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
|
541
|
+
switch (getRTLOffsetType()) {
|
|
542
|
+
case 'negative':
|
|
543
|
+
scrollOffset = -scrollLeft;
|
|
544
|
+
break;
|
|
545
|
+
case 'positive-descending':
|
|
546
|
+
scrollOffset = scrollWidth - clientWidth - scrollLeft;
|
|
547
|
+
break;
|
|
508
548
|
}
|
|
509
549
|
}
|
|
510
550
|
|
|
551
|
+
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
|
552
|
+
scrollOffset = Math.max(
|
|
553
|
+
0,
|
|
554
|
+
Math.min(scrollOffset, scrollWidth - clientWidth)
|
|
555
|
+
);
|
|
556
|
+
|
|
511
557
|
return {
|
|
512
558
|
isScrolling: true,
|
|
513
559
|
scrollDirection:
|
|
@@ -519,7 +565,7 @@ export default function createListComponent({
|
|
|
519
565
|
};
|
|
520
566
|
|
|
521
567
|
_onScrollVertical = (event: ScrollEvent): void => {
|
|
522
|
-
const { scrollTop } = event.currentTarget;
|
|
568
|
+
const { clientHeight, scrollHeight, scrollTop } = event.currentTarget;
|
|
523
569
|
this.setState(prevState => {
|
|
524
570
|
if (prevState.scrollOffset === scrollTop) {
|
|
525
571
|
// Scroll position may have been updated by cDM/cDU,
|
|
@@ -528,11 +574,17 @@ export default function createListComponent({
|
|
|
528
574
|
return null;
|
|
529
575
|
}
|
|
530
576
|
|
|
577
|
+
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
|
578
|
+
const scrollOffset = Math.max(
|
|
579
|
+
0,
|
|
580
|
+
Math.min(scrollTop, scrollHeight - clientHeight)
|
|
581
|
+
);
|
|
582
|
+
|
|
531
583
|
return {
|
|
532
584
|
isScrolling: true,
|
|
533
585
|
scrollDirection:
|
|
534
|
-
prevState.scrollOffset <
|
|
535
|
-
scrollOffset
|
|
586
|
+
prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
|
|
587
|
+
scrollOffset,
|
|
536
588
|
scrollUpdateWasRequested: false,
|
|
537
589
|
};
|
|
538
590
|
}, this._resetIsScrollingDebounced);
|
package/src/domHelpers.js
CHANGED
|
@@ -20,3 +20,53 @@ export function getScrollbarSize(recalculate?: boolean = false): number {
|
|
|
20
20
|
|
|
21
21
|
return size;
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
export type RTLOffsetType =
|
|
25
|
+
| 'negative'
|
|
26
|
+
| 'positive-descending'
|
|
27
|
+
| 'positive-ascending';
|
|
28
|
+
|
|
29
|
+
let cachedRTLResult: RTLOffsetType | null = null;
|
|
30
|
+
|
|
31
|
+
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
|
32
|
+
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
|
|
33
|
+
// Safari's elastic bounce makes detecting this even more complicated wrt potential false positives.
|
|
34
|
+
// The safest way to check this is to intentionally set a negative offset,
|
|
35
|
+
// and then verify that the subsequent "scroll" event matches the negative offset.
|
|
36
|
+
// If it does not match, then we can assume a non-standard RTL scroll implementation.
|
|
37
|
+
export function getRTLOffsetType(recalculate?: boolean = false): RTLOffsetType {
|
|
38
|
+
if (cachedRTLResult === null || recalculate) {
|
|
39
|
+
const outerDiv = document.createElement('div');
|
|
40
|
+
const outerStyle = outerDiv.style;
|
|
41
|
+
outerStyle.width = '50px';
|
|
42
|
+
outerStyle.height = '50px';
|
|
43
|
+
outerStyle.overflow = 'scroll';
|
|
44
|
+
outerStyle.direction = 'rtl';
|
|
45
|
+
|
|
46
|
+
const innerDiv = document.createElement('div');
|
|
47
|
+
const innerStyle = innerDiv.style;
|
|
48
|
+
innerStyle.width = '100px';
|
|
49
|
+
innerStyle.height = '100px';
|
|
50
|
+
|
|
51
|
+
outerDiv.appendChild(innerDiv);
|
|
52
|
+
|
|
53
|
+
((document.body: any): HTMLBodyElement).appendChild(outerDiv);
|
|
54
|
+
|
|
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
|
+
}
|
|
65
|
+
|
|
66
|
+
((document.body: any): HTMLBodyElement).removeChild(outerDiv);
|
|
67
|
+
|
|
68
|
+
return cachedRTLResult;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return cachedRTLResult;
|
|
72
|
+
}
|