Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix offset when applyWindowCorrection is used #699

Merged
merged 13 commits into from
Mar 21, 2022
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,
};