Skip to content

Commit

Permalink
Merge pull request #268 from MarkFalconbridge/SupportRTLInEdge
Browse files Browse the repository at this point in the history
Add support for RTL in Edge
  • Loading branch information
Brian Vaughn committed Jun 18, 2019
2 parents bf5572c + f346b7c commit bdc1f75
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 34 deletions.
40 changes: 23 additions & 17 deletions src/createGridComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}

Expand Down
33 changes: 20 additions & 13 deletions src/createListComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

Expand Down
21 changes: 17 additions & 4 deletions src/domHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,20 @@ 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).
// Safari's elastic bounce makes detecting this even more complicated wrt potential false positives.
// 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;
Expand All @@ -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);

Expand Down

0 comments on commit bdc1f75

Please sign in to comment.