diff --git a/src/createGridComponent.js b/src/createGridComponent.js index 38aea637..f2895b60 100644 --- a/src/createGridComponent.js +++ b/src/createGridComponent.js @@ -3,7 +3,7 @@ import memoizeOne from 'memoize-one'; import { createElement, PureComponent } from 'react'; import { cancelTimeout, requestTimeout } from './timer'; -import { getScrollbarSize, isRTLOffsetNegative } from './domHelpers'; +import { getScrollbarSize, getRTLOffsetType } from './domHelpers'; import type { TimeoutID } from './timer'; @@ -364,12 +364,17 @@ export default function createGridComponent({ // So we need to determine which browser behavior we're dealing with, and mimic it. const outerRef = ((this._outerRef: any): HTMLElement); if (direction === 'rtl') { - const isNegative = isRTLOffsetNegative(); - if (isNegative) { - outerRef.scrollLeft = -scrollLeft; - } else { - const { clientWidth, scrollWidth } = outerRef; - outerRef.scrollLeft = scrollWidth - clientWidth - scrollLeft; + switch (getRTLOffsetType()) { + case 'negative': + outerRef.scrollLeft = -scrollLeft; + break; + case 'positive-ascending': + outerRef.scrollLeft = scrollLeft; + break; + default: + const { clientWidth, scrollWidth } = outerRef; + outerRef.scrollLeft = scrollWidth - clientWidth - scrollLeft; + break; } } else { outerRef.scrollLeft = Math.max(0, scrollLeft); @@ -742,18 +747,19 @@ export default function createGridComponent({ const { direction } = this.props; + // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. + // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). + // It's also easier for this component if we convert offsets to the same format as they would be in for ltr. + // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it. let calculatedScrollLeft = scrollLeft; if (direction === 'rtl') { - const isNegative = isRTLOffsetNegative(); - - // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. - // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). - // It's also easier for this component if we convert offsets to the same format as they would be in for ltr. - // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it. - if (isNegative) { - calculatedScrollLeft = -scrollLeft; - } else { - calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft; + switch (getRTLOffsetType()) { + case 'negative': + calculatedScrollLeft = -scrollLeft; + break; + case 'positive-descending': + calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft; + break; } } diff --git a/src/createListComponent.js b/src/createListComponent.js index 5326d3b6..ae7851e9 100644 --- a/src/createListComponent.js +++ b/src/createListComponent.js @@ -3,7 +3,7 @@ import memoizeOne from 'memoize-one'; import { createElement, PureComponent } from 'react'; import { cancelTimeout, requestTimeout } from './timer'; -import { isRTLOffsetNegative } from './domHelpers'; +import { getRTLOffsetType } from './domHelpers'; import type { TimeoutID } from './timer'; @@ -251,18 +251,24 @@ export default function createListComponent({ if (scrollUpdateWasRequested && this._outerRef != null) { const outerRef = ((this._outerRef: any): HTMLElement); + // TODO Deprecate direction "horizontal" if (direction === 'horizontal' || layout === 'horizontal') { if (direction === 'rtl') { // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // So we need to determine which browser behavior we're dealing with, and mimic it. - const isNegative = isRTLOffsetNegative(); - if (isNegative) { - outerRef.scrollLeft = -scrollOffset; - } else { - const { clientWidth, scrollWidth } = outerRef; - outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset; + switch (getRTLOffsetType()) { + case 'negative': + outerRef.scrollLeft = -scrollOffset; + break; + case 'positive-ascending': + outerRef.scrollLeft = scrollOffset; + break; + default: + const { clientWidth, scrollWidth } = outerRef; + outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset; + break; } } else { outerRef.scrollLeft = scrollOffset; @@ -528,16 +534,17 @@ export default function createListComponent({ let scrollOffset = scrollLeft; if (direction === 'rtl') { - const isNegative = isRTLOffsetNegative(); - // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // It's also easier for this component if we convert offsets to the same format as they would be in for ltr. // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it. - if (isNegative) { - scrollOffset = -scrollLeft; - } else { - scrollOffset = scrollWidth - clientWidth - scrollLeft; + switch (getRTLOffsetType()) { + case 'negative': + scrollOffset = -scrollLeft; + break; + case 'positive-descending': + scrollOffset = scrollWidth - clientWidth - scrollLeft; + break; } } diff --git a/src/domHelpers.js b/src/domHelpers.js index 53a06cd6..1bdc9ddd 100644 --- a/src/domHelpers.js +++ b/src/domHelpers.js @@ -21,7 +21,12 @@ export function getScrollbarSize(recalculate?: boolean = false): number { return size; } -let cachedRTLResult: boolean | null = null; +export type RTLOffsetType = + | 'negative' + | 'positive-descending' + | 'positive-ascending'; + +let cachedRTLResult: RTLOffsetType | null = null; // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // 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; // The safest way to check this is to intentionally set a negative offset, // and then verify that the subsequent "scroll" event matches the negative offset. // If it does not match, then we can assume a non-standard RTL scroll implementation. -export function isRTLOffsetNegative(recalculate?: boolean = false): boolean { +export function getRTLOffsetType(recalculate?: boolean = false): RTLOffsetType { if (cachedRTLResult === null || recalculate) { const outerDiv = document.createElement('div'); const outerStyle = outerDiv.style; @@ -47,8 +52,16 @@ export function isRTLOffsetNegative(recalculate?: boolean = false): boolean { ((document.body: any): HTMLBodyElement).appendChild(outerDiv); - outerDiv.scrollLeft = -10; - cachedRTLResult = outerDiv.scrollLeft === -10; + if (outerDiv.scrollLeft > 0) { + cachedRTLResult = 'positive-descending'; + } else { + outerDiv.scrollLeft = 1; + if (outerDiv.scrollLeft === 0) { + cachedRTLResult = 'negative'; + } else { + cachedRTLResult = 'positive-ascending'; + } + } ((document.body: any): HTMLBodyElement).removeChild(outerDiv);