Skip to content
This repository was archived by the owner on Aug 13, 2024. It is now read-only.

Commit b928eb2

Browse files
committed
feat: implement type inferences
1 parent 42ccb43 commit b928eb2

File tree

7 files changed

+211
-77
lines changed

7 files changed

+211
-77
lines changed

example/src/App.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import * as React from 'react';
22
import { useState } from 'react';
33

44
import { Pressable, StyleSheet, Text, View } from 'react-native';
5-
import { FlatList } from '@sendbird/react-native-scrollview-enhancer';
5+
import {
6+
FlatList,
7+
ScrollView,
8+
} from '@sendbird/react-native-scrollview-enhancer';
69

710
let lastIdx = 0;
811
function messageFetcher(count: number) {
@@ -25,12 +28,22 @@ export default function App() {
2528

2629
return (
2730
<View style={styles.container}>
31+
<ScrollView>
32+
<Text>{'123'}</Text>
33+
</ScrollView>
2834
<FlatList
2935
inverted
3036
style={styles.box}
37+
onStartReachedThreshold={2}
3138
maintainVisibleContentPosition={{
32-
autoscrollToTopThreshold: 10,
33-
minIndexForVisible: 0,
39+
autoscrollToTopThreshold: 0,
40+
minIndexForVisible: 1,
41+
}}
42+
onStartReached={() => {
43+
setMessages((prev) => [...prev, ...messageFetcher(20)]);
44+
}}
45+
onEndReached={() => {
46+
setMessages((prev) => [...messageFetcher(20), ...prev]);
3447
}}
3548
onLayout={(e) => console.log('flatlist', e.nativeEvent)}
3649
data={messages}
@@ -53,7 +66,7 @@ export default function App() {
5366
<Pressable
5467
style={{ width: 100, height: 100 }}
5568
onPress={() => {
56-
setMessages((prev) => [...messageFetcher(5), ...prev]);
69+
setMessages((prev) => [...messageFetcher(20), ...prev]);
5770
}}
5871
>
5972
<Text>{'Load more'}</Text>
@@ -74,6 +87,5 @@ const styles = StyleSheet.create({
7487
},
7588
box: {
7689
flex: 1,
77-
backgroundColor: 'blue',
7890
},
7991
});

src/enhanceScrollView.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React, { ComponentType, useRef } from 'react';
2+
import {
3+
NativeScrollEvent,
4+
NativeSyntheticEvent,
5+
Platform,
6+
ScrollViewProps,
7+
} from 'react-native';
8+
import { ScrollViewEnhancerView } from './native';
9+
import type {
10+
EnhancedScrollViewProps,
11+
GetProps,
12+
ScrollViewEnhancerProps,
13+
} from './types';
14+
import { getRNVersion } from './utils';
15+
16+
export const enhanceScrollView = <
17+
T extends ComponentType<P>,
18+
P extends ScrollViewEnhancerProps = GetProps<T>
19+
>(
20+
ScrollViewComponent: T
21+
) => {
22+
const { minor } = getRNVersion();
23+
return React.forwardRef<T, P & EnhancedScrollViewProps>(
24+
(props: P, ref?: any): React.ReactElement | null => {
25+
const { renderScrollView } = useBiDirection(
26+
ScrollViewComponent,
27+
props,
28+
ref
29+
);
30+
31+
if (Platform.OS === 'android' && minor < 72) {
32+
return (
33+
<ScrollViewEnhancerView
34+
style={props.style}
35+
horizontal={props.horizontal}
36+
maintainVisibleContentPosition={
37+
props.maintainVisibleContentPosition
38+
}
39+
>
40+
{renderScrollView()}
41+
</ScrollViewEnhancerView>
42+
);
43+
} else {
44+
return renderScrollView();
45+
}
46+
}
47+
);
48+
};
49+
50+
export function useBiDirection<
51+
P extends ScrollViewProps & EnhancedScrollViewProps = {}
52+
>(Component: ComponentType<P>, props: P, ref?: any) {
53+
const innerRef = useRef<any>();
54+
const getRef = () => ref || innerRef;
55+
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
56+
props.onScroll?.(e);
57+
58+
console.log(getRef());
59+
};
60+
61+
const renderScrollView = () => {
62+
return <Component {...props} onScroll={onScroll} />;
63+
};
64+
65+
return {
66+
renderScrollView,
67+
};
68+
}

src/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {
2+
FlatList as RNFlatList,
3+
ScrollView as RNScrollView,
4+
SectionList as RNSectionList,
5+
} from 'react-native';
6+
import type { EnhancedScrollViewAbstraction } from './types';
7+
import { enhanceScrollView } from './enhanceScrollView';
8+
9+
const ScrollView = enhanceScrollView(RNScrollView);
10+
const FlatList = enhanceScrollView(RNFlatList) as unknown as
11+
| typeof RNFlatList
12+
| EnhancedScrollViewAbstraction<RNFlatList>;
13+
const SectionList = enhanceScrollView(RNSectionList) as unknown as
14+
| typeof RNSectionList
15+
| EnhancedScrollViewAbstraction<RNSectionList>;
16+
17+
export { enhanceScrollView, useBiDirection } from './enhanceScrollView';
18+
export { ScrollViewEnhancerView } from './native';
19+
export { ScrollView, FlatList, SectionList };

src/index.tsx

Lines changed: 0 additions & 72 deletions
This file was deleted.

src/native.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { Platform, requireNativeComponent, UIManager } from 'react-native';
3+
4+
import type { ScrollViewEnhancerProps } from './types';
5+
import { warningOnHorizontalScroll } from './utils';
6+
7+
const LINKING_ERROR =
8+
`The package 'react-native-scrollview-enhancer' doesn't seem to be linked. Make sure: \n\n` +
9+
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
10+
'- You rebuilt the app after installing the package\n' +
11+
'- You are not using Expo Go\n';
12+
13+
const ComponentName = 'ScrollViewEnhancerView';
14+
export const NativeView =
15+
UIManager.getViewManagerConfig(ComponentName) != null
16+
? requireNativeComponent<ScrollViewEnhancerProps>(ComponentName)
17+
: () => {
18+
throw new Error(LINKING_ERROR);
19+
};
20+
21+
export const ScrollViewEnhancerView = (props: ScrollViewEnhancerProps) => {
22+
if (__DEV__) warningOnHorizontalScroll(props);
23+
return <NativeView {...props} />;
24+
};

src/types.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type {
2+
ComponentType,
3+
ForwardRefExoticComponent,
4+
PropsWithChildren,
5+
PropsWithoutRef,
6+
RefAttributes,
7+
} from 'react';
8+
import type { ScrollViewProps, StyleProp, ViewStyle } from 'react-native';
9+
10+
export type ScrollViewEnhancerProps = PropsWithChildren<{
11+
style?: StyleProp<ViewStyle>;
12+
horizontal?: boolean | null;
13+
maintainVisibleContentPosition?: ScrollViewProps['maintainVisibleContentPosition'];
14+
}>;
15+
16+
export type EnhancedScrollViewProps = {
17+
/**
18+
* Called once when the scroll position gets within `onStartReachedThreshold`
19+
* from the logical start of the list.
20+
*/
21+
onStartReached?:
22+
| ((info: { distanceFromStart: number }) => void)
23+
| null
24+
| undefined;
25+
26+
/**
27+
* How far from the start (in units of visible length of the list) the leading edge of the
28+
* list must be from the start of the content to trigger the `onStartReached` callback.
29+
* Thus, a value of 0.5 will trigger `onStartReached` when the start of the content is
30+
* within half the visible length of the list.
31+
*/
32+
onStartReachedThreshold?: number | null | undefined;
33+
34+
/**
35+
* Called once when the scroll position gets within onEndReachedThreshold of the rendered content.
36+
*/
37+
onEndReached?:
38+
| ((info: { distanceFromEnd: number }) => void)
39+
| null
40+
| undefined;
41+
42+
/**
43+
* How far from the end (in units of visible length of the list) the bottom edge of the
44+
* list must be from the end of the content to trigger the `onEndReached` callback.
45+
* Thus, a value of 0.5 will trigger `onEndReached` when the end of the content is
46+
* within half the visible length of the list.
47+
*/
48+
onEndReachedThreshold?: number | null | undefined;
49+
50+
maintainVisibleContentPosition?: ScrollViewProps['maintainVisibleContentPosition'];
51+
};
52+
53+
export type GetProps<T> = T extends ComponentType<infer P> ? P : never;
54+
export type InjectProps<C, P> = C extends ComponentType<infer T>
55+
? ComponentType<T & P>
56+
: never;
57+
export type EnhancedScrollViewAbstraction<T> = ForwardRefExoticComponent<
58+
PropsWithoutRef<EnhancedScrollViewProps> & RefAttributes<T>
59+
>;

src/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Platform } from 'react-native';
2+
import type { ScrollViewEnhancerProps } from './types';
3+
4+
const pkgVersion = require('react-native/package.json').version as string;
5+
const [major, minor, patch] = pkgVersion.split('.').map((it) => Number(it)) as [
6+
number,
7+
number,
8+
number
9+
];
10+
const parsedVersion = { major, minor, patch };
11+
12+
let _warningCalled = false;
13+
export function warningOnHorizontalScroll(props: ScrollViewEnhancerProps) {
14+
if (props.horizontal && Platform.OS === 'android' && !_warningCalled) {
15+
_warningCalled = true;
16+
console.warn(
17+
'ScrollViewEnhancerView does not support horizontal direction'
18+
);
19+
}
20+
}
21+
22+
export function getRNVersion() {
23+
return parsedVersion;
24+
}

0 commit comments

Comments
 (0)