Skip to content

Commit e617ce4

Browse files
authored
Infra/generify faded scroll view (#1842)
* FadedScrollView make more generic * Move FadedScrollView
1 parent a441935 commit e617ce4

File tree

5 files changed

+186
-112
lines changed

5 files changed

+186
-112
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { ScrollViewProps } from 'react-native';
3+
import { FaderProps } from '../fader';
4+
export declare type FadedScrollViewProps = ScrollViewProps & {
5+
/**
6+
* Show a fader at the start of the scroll
7+
*/
8+
showStartFader?: boolean;
9+
/**
10+
* Additional props for the start fader
11+
*/
12+
startFaderProps?: Omit<FaderProps, 'visible' | 'position'>;
13+
/**
14+
* Show a fader at the end of the scroll
15+
*/
16+
showEndFader?: boolean;
17+
/**
18+
* Additional props for the end fader
19+
*/
20+
endFaderProps?: Omit<FaderProps, 'visible' | 'position'>;
21+
/**
22+
* Use the react-native-gesture-handler version, useful when using react-native-reanimated
23+
*/
24+
useGesture?: boolean;
25+
children?: React.ReactNode | React.ReactNode[];
26+
};
27+
interface Statics {
28+
scrollTo(y?: number | {
29+
x?: number | undefined;
30+
y?: number | undefined;
31+
animated?: boolean | undefined;
32+
}, x?: number, animated?: boolean): void;
33+
isScrollEnabled: () => boolean;
34+
}
35+
declare const _default: React.ComponentType<FadedScrollViewProps> & Statics;
36+
export default _default;

generatedTypes/src/components/tabController/FadedScrollView.d.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import React, {useCallback, useImperativeHandle} from 'react';
2+
import {
3+
ScrollView as RNScrollView,
4+
ScrollViewProps,
5+
NativeSyntheticEvent,
6+
NativeScrollEvent,
7+
LayoutChangeEvent
8+
} from 'react-native';
9+
import Fader, {FaderProps} from '../fader';
10+
import useScrollEnabler from '../../hooks/useScrollEnabler';
11+
import useScrollReached from '../../hooks/useScrollReached';
12+
import {forwardRef, ForwardRefInjectedProps} from '../../commons/new';
13+
import {ScrollView as GestureScrollView} from 'react-native-gesture-handler';
14+
15+
export type FadedScrollViewProps = ScrollViewProps & {
16+
/**
17+
* Show a fader at the start of the scroll
18+
*/
19+
showStartFader?: boolean;
20+
/**
21+
* Additional props for the start fader
22+
*/
23+
startFaderProps?: Omit<FaderProps, 'visible' | 'position'>;
24+
/**
25+
* Show a fader at the end of the scroll
26+
*/
27+
showEndFader?: boolean;
28+
/**
29+
* Additional props for the end fader
30+
*/
31+
endFaderProps?: Omit<FaderProps, 'visible' | 'position'>;
32+
/**
33+
* Use the react-native-gesture-handler version, useful when using react-native-reanimated
34+
*/
35+
useGesture?: boolean;
36+
children?: React.ReactNode | React.ReactNode[];
37+
};
38+
39+
type Props = FadedScrollViewProps & ForwardRefInjectedProps;
40+
interface Statics {
41+
scrollTo(
42+
y?: number | {x?: number | undefined; y?: number | undefined; animated?: boolean | undefined},
43+
x?: number,
44+
animated?: boolean
45+
): void;
46+
isScrollEnabled: () => boolean;
47+
}
48+
49+
const FadedScrollView = (props: Props) => {
50+
const {
51+
children,
52+
onScroll: propsOnScroll,
53+
onContentSizeChange: propsOnContentSizeChange,
54+
onLayout: propsOnLayout,
55+
horizontal: propsHorizontal,
56+
showStartFader,
57+
startFaderProps,
58+
showEndFader,
59+
endFaderProps,
60+
useGesture,
61+
...others
62+
} = props;
63+
const ScrollView = useGesture ? GestureScrollView : RNScrollView;
64+
const scrollViewRef = React.createRef<typeof ScrollView>();
65+
const horizontal = propsHorizontal ?? false;
66+
const {onContentSizeChange, onLayout, scrollEnabled} = useScrollEnabler({horizontal});
67+
const {
68+
onScroll: onScrollReached,
69+
isScrollAtStart,
70+
isScrollAtEnd
71+
} = useScrollReached({
72+
horizontal,
73+
threshold: 50
74+
});
75+
76+
const showStart = (showStartFader && scrollEnabled && !isScrollAtStart) ?? false;
77+
const showEnd = (showEndFader && scrollEnabled && !isScrollAtEnd) ?? false;
78+
79+
const onScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
80+
onScrollReached(event);
81+
propsOnScroll?.(event);
82+
},
83+
[onScrollReached, propsOnScroll]);
84+
85+
const _onContentSizeChange = useCallback((w: number, h: number) => {
86+
propsOnContentSizeChange?.(w, h);
87+
onContentSizeChange?.(w, h);
88+
},
89+
[propsOnContentSizeChange, onContentSizeChange]);
90+
91+
const _onLayout = useCallback((event: LayoutChangeEvent) => {
92+
propsOnLayout?.(event);
93+
onLayout?.(event);
94+
},
95+
[propsOnLayout, onLayout]);
96+
97+
const isScrollEnabled = () => {
98+
return scrollEnabled;
99+
};
100+
101+
useImperativeHandle(props.forwardedRef, () => ({
102+
// @ts-expect-error
103+
scrollTo: (...data: any) => scrollViewRef.current?.scrollTo?.(...data),
104+
isScrollEnabled
105+
}));
106+
107+
if (children) {
108+
return (
109+
<>
110+
<ScrollView
111+
scrollEventThrottle={16}
112+
decelerationRate={'fast'}
113+
{...others}
114+
horizontal={horizontal}
115+
scrollEnabled={scrollEnabled}
116+
onContentSizeChange={_onContentSizeChange}
117+
onLayout={_onLayout}
118+
onScroll={onScroll}
119+
ref={scrollViewRef}
120+
>
121+
{children}
122+
</ScrollView>
123+
<Fader
124+
visible={showStart}
125+
position={horizontal ? Fader.position.START : Fader.position.TOP}
126+
{...startFaderProps}
127+
/>
128+
<Fader
129+
visible={showEnd}
130+
position={horizontal ? Fader.position.END : Fader.position.BOTTOM}
131+
{...endFaderProps}
132+
/>
133+
</>
134+
);
135+
}
136+
137+
return null;
138+
};
139+
140+
FadedScrollView.displayName = 'IGNORE';
141+
// @ts-expect-error
142+
export default forwardRef<FadedScrollViewProps, Statics>(FadedScrollView);

src/components/tabController/FadedScrollView.tsx

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

src/components/tabController/TabBar.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import TabBarItem, {TabControllerItemProps} from './TabBarItem';
88
import {Constants, asBaseComponent, forwardRef, BaseComponentInjectedProps, ForwardRefInjectedProps} from '../../commons/new';
99
import View from '../view';
1010
import {Colors, Spacings, Typography} from '../../style';
11-
import FadedScrollView from './FadedScrollView';
11+
import FadedScrollView from '../fadedScrollView';
1212

1313
import useScrollToItem from './useScrollToItem';
1414
import {orientations} from '../../commons/Constants';
@@ -117,6 +117,8 @@ interface Props extends TabControllerBarProps, BaseComponentInjectedProps, Forwa
117117
children?: ChildProps[] | ChildProps;
118118
}
119119

120+
const FADER_PROPS = {size: 76};
121+
120122
/**
121123
* @description: TabController's TabBar component
122124
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.tsx
@@ -260,7 +262,6 @@ const TabBar = (props: Props) => {
260262
}, [containerWidth]);
261263

262264
useDidUpdate(() => {
263-
// @ts-expect-error TODO: fix forwardRef Statics
264265
if (tabBar.current?.isScrollEnabled()) {
265266
focusIndex(currentPage.value);
266267
} else {
@@ -275,6 +276,11 @@ const TabBar = (props: Props) => {
275276
// @ts-expect-error
276277
ref={tabBar}
277278
horizontal
279+
showsHorizontalScrollIndicator={false}
280+
showStartFader
281+
startFaderProps={FADER_PROPS}
282+
showEndFader
283+
endFaderProps={FADER_PROPS}
278284
contentContainerStyle={scrollViewContainerStyle}
279285
testID={testID}
280286
onContentSizeChange={onContentSizeChange}

0 commit comments

Comments
 (0)