Skip to content

Commit c8d3ed5

Browse files
ScrollBar - TS migration (#1078)
* ScrollBar - TS migration * exporting * adding 'any' type * moving extand to Props * adding //@ts-ignore to forwardedRef.current * Moving 'ForwardRefInjectedProps' to type * Exporting only ScrollBarProps while passing Props to the component * Fix ScrollBar exported typings Co-authored-by: Ethan Sharabi <[email protected]>
1 parent 23ce1bf commit c8d3ed5

File tree

4 files changed

+171
-57
lines changed

4 files changed

+171
-57
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React, { Component } from 'react';
2+
import { Animated, FlatListProps, ImageSourcePropType, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent } from 'react-native';
3+
import { ForwardRefInjectedProps } from '../../commons/new';
4+
interface Props extends FlatListProps<any>, ForwardRefInjectedProps {
5+
/**
6+
* Whether to use a FlatList. NOTE: you must pass 'data' and 'renderItem' props as well
7+
*/
8+
useList?: boolean;
9+
/**
10+
* The element to use as a container, instead of a View
11+
*/
12+
containerView?: JSX.Element;
13+
/**
14+
* The props to pass the container
15+
*/
16+
containerProps?: object;
17+
/**
18+
* The component's height
19+
*/
20+
height?: number;
21+
/**
22+
* The gradient's height, defaults to the component's height
23+
*/
24+
gradientHeight?: number;
25+
/**
26+
* The gradient's width
27+
*/
28+
gradientWidth?: number;
29+
/**
30+
* The gradient's margins for the edge
31+
*/
32+
gradientMargins?: number;
33+
/**
34+
* The gradient's tint color
35+
*/
36+
gradientColor?: string;
37+
/**
38+
* The gradient's image, instead of the default image.
39+
* NOTE: pass an image for the right-hand side and it will be flipped to match the left-hand side
40+
*/
41+
gradientImage?: ImageSourcePropType;
42+
/**
43+
* The index to currently focus on
44+
*/
45+
focusIndex?: number;
46+
}
47+
export declare type ScrollBarProps = Props;
48+
export declare type State = {
49+
gradientOpacity: Animated.Value;
50+
gradientOpacityLeft: Animated.Value;
51+
};
52+
/**
53+
* @description: Scrollable container with animated gradient overlay for horizontal scroll
54+
* @extends: ScrollView / FlatList
55+
*/
56+
declare class ScrollBar extends Component<Props, State> {
57+
static displayName: string;
58+
static defaultProps: {
59+
gradientWidth: number;
60+
gradientMargins: number;
61+
gradientColor: string;
62+
focusIndex: number;
63+
};
64+
static Item: typeof Item;
65+
constructor(props: Props);
66+
private scrollbar;
67+
private itemsLayouts;
68+
private contentOffset;
69+
private scrollContentWidth;
70+
private containerWidth;
71+
componentDidUpdate(prevProps: Props): void;
72+
focusIndex: (index?: number) => void;
73+
animateGradientOpacity: (offsetX: number, contentWidth: number, containerWidth: number) => void;
74+
onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
75+
onContentSizeChange: (contentWidth: number, contentHeight: number) => void;
76+
onLayout: (event: LayoutChangeEvent) => void;
77+
onItemLayout: ({ layout, index }: any) => void;
78+
renderScrollable(): JSX.Element;
79+
renderGradient(left: boolean): JSX.Element;
80+
render(): JSX.Element;
81+
}
82+
declare const Item: {
83+
({ children, index, onLayout }: any): JSX.Element;
84+
displayName: string;
85+
};
86+
declare const _default: React.ComponentClass<Props & {
87+
useCustomTheme?: boolean | undefined; /**
88+
* Whether to use a FlatList. NOTE: you must pass 'data' and 'renderItem' props as well
89+
*/
90+
}, any> & typeof ScrollBar;
91+
export default _default;

generatedTypes/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export {default as RadioGroup, RadioGroupPropTypes, RadioGroupProps} from './com
4444
export {default as Switch, SwitchProps} from './components/switch';
4545
export {default as TabController, TabControllerProps, TabControllerItemProps} from './components/tabController';
4646
export {default as TabBar, TabBarProps} from './components/TabBar';
47+
export {default as ScrollBar, ScrollBarProps} from './components/ScrollBar';
4748
export {default as Fader, FaderProps, FaderPosition} from './components/fader';
4849
export {default as ExpandableSection, ExpandableSectionProps} from './components/expandableSection';
4950
export {default as Modal, ModalProps, ModalTopBarProps} from './components/modal';
@@ -114,7 +115,6 @@ export const KeyboardAwareListView;
114115
export const KeyboardAwareScrollView;
115116
export const SafeAreaInsetsManager;
116117
export const SafeAreaSpacerView;
117-
export const ScrollBar;
118118
export const SelectableComponent;
119119
export const TextField;
120120
export const Wizard;

src/components/scrollBar/index.js renamed to src/components/scrollBar/index.tsx

Lines changed: 78 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,83 @@
11
import _ from 'lodash';
2-
import PropTypes from 'prop-types';
3-
import React, {useCallback} from 'react';
4-
import {Animated, ScrollView, FlatList} from 'react-native';
2+
import React, {Component, useCallback} from 'react';
3+
import {
4+
Animated,
5+
ScrollView,
6+
FlatList,
7+
FlatListProps,
8+
ImageSourcePropType,
9+
NativeSyntheticEvent,
10+
NativeScrollEvent,
11+
LayoutChangeEvent
12+
} from 'react-native';
513
import {Constants} from '../../helpers';
614
import {Colors} from '../../style';
7-
import {BaseComponent, forwardRef} from '../../commons';
15+
import {asBaseComponent, forwardRef, ForwardRefInjectedProps} from '../../commons/new';
816
import View from '../view';
917
import Image from '../image';
1018

11-
const GRADIENT_WIDTH = 76;
12-
const defaultImage = () => require('./assets/gradientOverlay.png');
13-
14-
/**
15-
* @description: Scrollable container with animated gradient overlay for horizontal scroll
16-
* @extends: ScrollView / FlatList
17-
*/
18-
19-
class ScrollBar extends BaseComponent {
20-
static displayName = 'ScrollBar';
2119

22-
static propTypes = {
23-
...ScrollView.propTypes,
24-
...FlatList.propTypes,
25-
/**
20+
export interface ScrollBarProps extends FlatListProps<any> {
21+
/**
2622
* Whether to use a FlatList. NOTE: you must pass 'data' and 'renderItem' props as well
2723
*/
28-
useList: PropTypes.bool,
24+
useList?: boolean,
2925
/**
3026
* The element to use as a container, instead of a View
3127
*/
32-
containerView: PropTypes.oneOfType([PropTypes.element, PropTypes.elementType]),
28+
containerView?: JSX.Element,
3329
/**
3430
* The props to pass the container
3531
*/
36-
containerProps: PropTypes.object,
32+
containerProps?: object,
3733
/**
3834
* The component's height
3935
*/
40-
height: PropTypes.number,
36+
height?: number,
4137
/**
4238
* The gradient's height, defaults to the component's height
4339
*/
44-
gradientHeight: PropTypes.number,
40+
gradientHeight?: number,
4541
/**
4642
* The gradient's width
4743
*/
48-
gradientWidth: PropTypes.number,
44+
gradientWidth?: number,
4945
/**
5046
* The gradient's margins for the edge
5147
*/
52-
gradientMargins: PropTypes.number,
48+
gradientMargins?: number,
5349
/**
5450
* The gradient's tint color
5551
*/
56-
gradientColor: PropTypes.string,
52+
gradientColor?: string,
5753
/**
5854
* The gradient's image, instead of the default image.
5955
* NOTE: pass an image for the right-hand side and it will be flipped to match the left-hand side
6056
*/
61-
gradientImage: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
57+
gradientImage?: ImageSourcePropType,
6258
/**
6359
* The index to currently focus on
6460
*/
65-
focusIndex: PropTypes.number
66-
};
61+
focusIndex?: number
62+
};
63+
type Props = ScrollBarProps & ForwardRefInjectedProps;
64+
65+
export type State = {
66+
gradientOpacity: Animated.Value,
67+
gradientOpacityLeft: Animated.Value
68+
};
69+
70+
71+
const GRADIENT_WIDTH = 76;
72+
const defaultImage = () => require('./assets/gradientOverlay.png');
73+
74+
/**
75+
* @description: Scrollable container with animated gradient overlay for horizontal scroll
76+
* @extends: ScrollView / FlatList
77+
*/
78+
79+
class ScrollBar extends Component<Props, State> {
80+
static displayName = 'ScrollBar';
6781

6882
static defaultProps = {
6983
gradientWidth: GRADIENT_WIDTH,
@@ -72,27 +86,31 @@ class ScrollBar extends BaseComponent {
7286
focusIndex: 0
7387
};
7488

75-
constructor(props) {
89+
static Item: typeof Item;
90+
91+
constructor(props: Props) {
7692
super(props);
7793

7894
this.state = {
7995
gradientOpacity: new Animated.Value(0),
8096
gradientOpacityLeft: new Animated.Value(0)
8197
};
82-
83-
this.scrollContentWidth = undefined;
84-
this.itemsLayouts = {};
85-
this.contentOffset = 0;
8698
}
8799

88-
componentDidUpdate(prevProps, prevState) {
100+
private scrollbar: any = undefined;
101+
private itemsLayouts: any = {};
102+
private contentOffset: number = 0;
103+
private scrollContentWidth: number = 0;
104+
private containerWidth: number = 0;
105+
106+
componentDidUpdate(prevProps: Props) {
89107
const {focusIndex} = this.props;
90-
if (focusIndex && prevProps.focusIndex !== focusIndex) {
108+
if (prevProps.focusIndex !== focusIndex) {
91109
this.focusIndex(focusIndex);
92110
}
93111
}
94112

95-
focusIndex = index => {
113+
focusIndex = (index: number = 0) => {
96114
const focusedItemLayout = this.itemsLayouts[index];
97115
if (focusedItemLayout) {
98116
const {x, width} = focusedItemLayout;
@@ -105,7 +123,7 @@ class ScrollBar extends BaseComponent {
105123
}
106124
};
107125

108-
animateGradientOpacity = (offsetX, contentWidth, containerWidth) => {
126+
animateGradientOpacity = (offsetX: number, contentWidth: number, containerWidth: number) => {
109127
const overflow = contentWidth - containerWidth;
110128
const newValue = offsetX > 0 && offsetX >= overflow - 1 ? 0 : 1;
111129
const newValueLeft = offsetX > 0 ? 1 : 0;
@@ -124,7 +142,7 @@ class ScrollBar extends BaseComponent {
124142
]).start();
125143
};
126144

127-
onScroll = event => {
145+
onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
128146
const {layoutMeasurement, contentOffset, contentSize} = event.nativeEvent;
129147
this.contentOffset = contentOffset.x;
130148
const offsetX = contentOffset.x;
@@ -136,7 +154,7 @@ class ScrollBar extends BaseComponent {
136154
_.invoke(this.props, 'onScroll', event);
137155
};
138156

139-
onContentSizeChange = (contentWidth, contentHeight) => {
157+
onContentSizeChange = (contentWidth: number, contentHeight: number) => {
140158
if (this.scrollContentWidth !== contentWidth) {
141159
this.scrollContentWidth = contentWidth;
142160

@@ -149,34 +167,37 @@ class ScrollBar extends BaseComponent {
149167
}
150168
};
151169

152-
onLayout = ({nativeEvent}) => {
153-
this.containerWidth = nativeEvent.layout.width;
170+
onLayout = (event: LayoutChangeEvent) => {
171+
this.containerWidth = event.nativeEvent.layout.width;
154172

155173
// 1 - for race condition, in case onContentSizeChange() is called before
156174
// 0 - for containerWidth change, when onContentSizeChange() is called first
157175
this.setState({gradientOpacity: new Animated.Value(this.scrollContentWidth > this.containerWidth ? 1 : 0)});
158176
};
159177

160-
onItemLayout = ({layout, index}) => {
178+
onItemLayout = ({layout, index}: any) => {
161179
this.itemsLayouts[index] = layout;
162-
if (_.keys(this.itemsLayouts).length === this.props.children.length) {
163-
this.focusIndex(this.props.focusIndex);
180+
181+
const {children, focusIndex} = this.props;
182+
if (children && _.keys(this.itemsLayouts).length === _.keys(children).length) {
183+
this.focusIndex(focusIndex);
164184
}
165185
};
166186

167187
renderScrollable() {
168188
const {useList, forwardedRef, children} = this.props;
169-
const Component = useList ? FlatList : ScrollView;
189+
const Component: any = useList ? FlatList : ScrollView;
170190

171191
return (
172192
<Component
173193
scrollEventThrottle={100}
174-
{...this.getThemeProps()}
175-
ref={r => {
194+
{...this.props}
195+
ref={(r: any) => {
176196
this.scrollbar = r;
177197
if (_.isFunction(forwardedRef)) {
178198
forwardedRef(r);
179199
} else if (_.isObject(forwardedRef)) {
200+
//@ts-ignore
180201
forwardedRef.current = r;
181202
}
182203
}}
@@ -197,9 +218,9 @@ class ScrollBar extends BaseComponent {
197218
);
198219
}
199220

200-
renderGradient(left) {
221+
renderGradient(left: boolean) {
201222
const {gradientOpacity, gradientOpacityLeft} = this.state;
202-
const {gradientWidth, gradientHeight, gradientMargins, height, gradientColor, gradientImage} = this.getThemeProps();
223+
const {gradientWidth, gradientHeight, gradientMargins, height, gradientColor, gradientImage} = this.props;
203224
const imageTransform = Constants.isRTL ? (left ? undefined : [{scaleX: -1}]) : left ? [{scaleX: -1}] : undefined;
204225
const heightToUse = gradientHeight || height || '100%';
205226
return (
@@ -229,23 +250,23 @@ class ScrollBar extends BaseComponent {
229250
}
230251

231252
render() {
232-
const {containerView, containerProps} = this.getThemeProps();
233-
const Container = containerView || View;
253+
const {containerView, containerProps} = this.props;
254+
const Container: any = containerView || View;
234255

235256
return (
236257
<Container row {...containerProps} onLayout={this.onLayout}>
237258
{this.renderScrollable()}
238-
{this.renderGradient()}
259+
{this.renderGradient(false)}
239260
{this.renderGradient(true)}
240261
</Container>
241262
);
242263
}
243264
}
244265

245-
const Item = ({children, index, onLayout}) => {
266+
const Item = ({children, index, onLayout}: any) => {
246267
const onItemLayout = useCallback(({nativeEvent: {layout}}) => {
247268
onLayout({layout, index});
248-
});
269+
}, [children]);
249270

250271
return (
251272
<View flexG onLayout={onItemLayout}>
@@ -256,4 +277,5 @@ const Item = ({children, index, onLayout}) => {
256277

257278
Item.displayName = 'IGNORE';
258279
ScrollBar.Item = Item;
259-
export default forwardRef(ScrollBar);
280+
export default asBaseComponent<ScrollBarProps, typeof ScrollBar>(forwardRef(ScrollBar));
281+

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export {default as RadioGroup, RadioGroupPropTypes, RadioGroupProps} from './com
3838
export {default as Switch, SwitchProps} from './components/switch';
3939
export {default as TabController, TabControllerProps, TabControllerItemProps} from './components/tabController';
4040
export {default as TabBar, TabBarProps} from './components/TabBar';
41+
export {default as ScrollBar, ScrollBarProps} from './components/ScrollBar';
4142
export {default as Fader, FaderProps, FaderPosition} from './components/fader';
4243
export {default as ExpandableSection, ExpandableSectionProps} from './components/expandableSection';
4344
export {default as Modal, ModalProps, ModalTopBarProps} from './components/modal';

0 commit comments

Comments
 (0)