Skip to content

Commit

Permalink
Fix offset when applyWindowCorrection is used (#699)
Browse files Browse the repository at this point in the history
* Accounting for correcting in scroll requests

* clearing stable id map to avoid collisions

* Fixes stable id and introduces new API for layout animations

* modifying existing method instead of a new one

* Added warning if animations render is requested on pagination

* updated docs

* adding correction config

* handled item scroll

* exported types

* fix bring to focus

Co-authored-by: Talha Naqvi <talha.naqvi@shopify.com>
  • Loading branch information
naqvitalha and naqvitalha authored Mar 21, 2022
1 parent 3f9a4f4 commit 9f1c9e9
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ In case you cannot determine heights of items in advance just set `forceNonDeter
| scrollViewProps | No | object | For all props that need to be proxied to inner/external scrollview. Put them in an object and they'll be spread and passed down. |
| layoutSize | No | Dimension | Will prevent the initial empty render required to compute the size of the listview and use these dimensions to render list items in the first render itself. This is useful for cases such as server side rendering. The prop canChangeSize has to be set to true if the size can be changed after rendering. Note that this is not the scroll view size and is used solely for layouting. |
| onItemLayout | No | number | A callback function that is executed when an item of the recyclerListView (at an index) has been layout. This can also be used as a proxy to itemsRendered kind of callbacks. |
| windowCorrectionConfig | No | object | Used to specify is window correction config and whether it should be applied to some scroll events |

For full feature set have a look at prop definitions of [RecyclerListView](https://github.com/Flipkart/recyclerlistview/blob/21049cc89ad606ec9fe8ea045dc73732ff29eac9/src/core/RecyclerListView.tsx#L540-L634)
(bottom of the file). All `ScrollView` features like `RefreshControl` also work out of the box.
Expand Down
51 changes: 41 additions & 10 deletions src/core/RecyclerListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,20 @@ export interface RecyclerListViewProps {
scrollViewProps?: object;
applyWindowCorrection?: (offsetX: number, offsetY: number, windowCorrection: WindowCorrection) => void;
onItemLayout?: (index: number) => void;
windowCorrectionConfig?: { value?: WindowCorrection, applyToInitialOffset?: boolean, applyToItemScroll?: boolean };
}

export interface RecyclerListViewState {
renderStack: RenderStack;
internalSnapshot: Record<string, object>;
}

export interface WindowCorrectionConfig {
value: WindowCorrection;
applyToInitialOffset: boolean;
applyToItemScroll: boolean;
}

export default class RecyclerListView<P extends RecyclerListViewProps, S extends RecyclerListViewState> extends ComponentCompat<P, S> {
public static defaultProps = {
canChangeSize: false,
Expand Down Expand Up @@ -155,7 +162,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
private _initialOffset = 0;
private _cachedLayouts?: Layout[];
private _scrollComponent: BaseScrollComponent | null = null;
private _windowCorrection: WindowCorrection;
private _windowCorrectionConfig: WindowCorrectionConfig;

//If the native content container is used, then positions of the list items are changed on the native side. The animated library used
//by the default item animator also changes the same positions which could lead to inconsistency. Hence, the base item animator which
Expand All @@ -170,9 +177,25 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
return this.props.dataProvider.getStableId(index);
}, !props.disableRecycling);

this._windowCorrection = {
startCorrection: 0, endCorrection: 0, windowShift: 0,
};
if (this.props.windowCorrectionConfig) {
let windowCorrection;
if (this.props.windowCorrectionConfig.value) {
windowCorrection = this.props.windowCorrectionConfig.value;
} else {
windowCorrection = { startCorrection: 0, endCorrection: 0, windowShift: 0 };
}
this._windowCorrectionConfig = {
applyToItemScroll: !!this.props.windowCorrectionConfig.applyToItemScroll,
applyToInitialOffset: !!this.props.windowCorrectionConfig.applyToInitialOffset,
value: windowCorrection,
};
} else {
this._windowCorrectionConfig = {
applyToItemScroll: false,
applyToInitialOffset: false,
value: { startCorrection: 0, endCorrection: 0, windowShift: 0 },
};
}
this._getContextFromContextProvider(props);
if (props.layoutSize) {
this._layout.height = props.layoutSize.height;
Expand Down Expand Up @@ -241,7 +264,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
const layoutManager = this._virtualRenderer.getLayoutManager();
if (layoutManager) {
const offsets = layoutManager.getOffsetForIndex(index);
this.scrollToOffset(offsets.x, offsets.y, animate);
this.scrollToOffset(offsets.x, offsets.y, animate, this._windowCorrectionConfig.applyToItemScroll);
} else {
console.warn(Messages.WARN_SCROLL_TO_INDEX); //tslint:disable-line
}
Expand All @@ -255,7 +278,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
public bringToFocus(index: number, animate?: boolean): void {
const listSize = this.getRenderedSize();
const itemLayout = this.getLayout(index);
const currentScrollOffset = this.getCurrentScrollOffset();
const currentScrollOffset = this.getCurrentScrollOffset() + this._windowCorrectionConfig.value.windowShift;
const {isHorizontal} = this.props;
if (itemLayout) {
const mainAxisLayoutDimen = isHorizontal ? itemLayout.width : itemLayout.height;
Expand All @@ -268,7 +291,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
const viewEndPos = mainAxisLayoutPos + mainAxisLayoutDimen;
if (viewEndPos > screenEndPos) {
const offset = viewEndPos - screenEndPos;
this.scrollToOffset(0, offset + currentScrollOffset, animate);
this.scrollToOffset(offset + currentScrollOffset, offset + currentScrollOffset, animate, true);
}
}
}
Expand Down Expand Up @@ -298,12 +321,16 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
this.scrollToIndex(lastIndex, animate);
}

public scrollToOffset = (x: number, y: number, animate: boolean = false): void => {
// useWindowCorrection specifies if correction should be applied to these offsets in case you implement
// `applyWindowCorrection` method
public scrollToOffset = (x: number, y: number, animate: boolean = false, useWindowCorrection: boolean = false): void => {
if (this._scrollComponent) {
if (this.props.isHorizontal) {
y = 0;
x = useWindowCorrection ? x - this._windowCorrectionConfig.value.windowShift : x;
} else {
x = 0;
y = useWindowCorrection ? y - this._windowCorrectionConfig.value.windowShift : y;
}
this._scrollComponent.scrollTo(x, y, animate);
}
Expand Down Expand Up @@ -430,7 +457,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
} else {
offset.x = 0;
}
this.scrollToOffset(offset.x, offset.y, false);
this.scrollToOffset(offset.x, offset.y, false, this._windowCorrectionConfig.applyToInitialOffset);
if (this._pendingRenderStack) {
this._renderStackWhenReady(this._pendingRenderStack);
this._pendingRenderStack = undefined;
Expand Down Expand Up @@ -610,7 +637,8 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
}

private _getWindowCorrection(offsetX: number, offsetY: number, props: RecyclerListViewProps): WindowCorrection {
return (props.applyWindowCorrection && props.applyWindowCorrection(offsetX, offsetY, this._windowCorrection)) || this._windowCorrection;
return (props.applyWindowCorrection && props.applyWindowCorrection(offsetX, offsetY, this._windowCorrectionConfig.value))
|| this._windowCorrectionConfig.value;
}

private _assertDependencyPresence(props: RecyclerListViewProps): void {
Expand Down Expand Up @@ -871,4 +899,7 @@ RecyclerListView.propTypes = {
// but there is a catch here, since there might be a pending relayout due to which the queried layout might not be precise.
// Caution: RLV only listens to layout changes if forceNonDeterministicRendering is true
onItemLayout: PropTypes.func,

//Used to specify is window correction config and whether it should be applied to some scroll events
windowCorrectionConfig: PropTypes.object,
};
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import ContextProvider from "./core/dependencies/ContextProvider";
import DataProvider, { BaseDataProvider } from "./core/dependencies/DataProvider";
import { BaseLayoutProvider, Dimension, LayoutProvider } from "./core/dependencies/LayoutProvider";
import { GridLayoutProvider } from "./core/dependencies/GridLayoutProvider";
import RecyclerListView, { OnRecreateParams, RecyclerListViewProps } from "./core/RecyclerListView";
import RecyclerListView, { OnRecreateParams, RecyclerListViewProps, WindowCorrectionConfig } from "./core/RecyclerListView";
import BaseScrollView from "./core/scrollcomponent/BaseScrollView";
import { BaseItemAnimator } from "./core/ItemAnimator";
import { AutoScroll } from "./utils/AutoScroll";
Expand All @@ -11,6 +11,7 @@ import { GridLayoutManager } from "./core/layoutmanager/GridLayoutManager";
import ProgressiveListView from "./core/ProgressiveListView";
import { DebugHandlers } from "./core/devutils/debughandlers/DebugHandlers";
import { ComponentCompat } from "./utils/ComponentCompat";
import { WindowCorrection } from "./core/ViewabilityTracker";

export {
ContextProvider,
Expand All @@ -34,4 +35,6 @@ export {
DebugHandlers,
BaseDataProvider,
ComponentCompat,
WindowCorrection,
WindowCorrectionConfig,
};

0 comments on commit 9f1c9e9

Please sign in to comment.