Skip to content

Commit 59f7572

Browse files
committed
TabController - move to typescript (fix typescript)
1 parent 8239fdb commit 59f7572

File tree

2 files changed

+123
-46
lines changed

2 files changed

+123
-46
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 TabBar from './TabBar';
3+
import TabBarItem, { TabBarItemProps } from './TabBarItem';
4+
import TabPage from './TabPage';
5+
import PageCarousel from './PageCarousel';
6+
interface TabControllerProps {
7+
/**
8+
* The list of tab bar items
9+
*/
10+
items: TabBarItemProps[];
11+
/**
12+
* Initial selected index
13+
*/
14+
selectedIndex: number;
15+
/**
16+
* callback for when index has change (will not be called on ignored items)
17+
*/
18+
onChangeIndex: (index: number) => void;
19+
/**
20+
* When using TabController.PageCarousel this should be turned on
21+
*/
22+
asCarousel?: boolean;
23+
/**
24+
* Pass for custom carousel page width
25+
*/
26+
carouselPageWidth?: number;
27+
}
28+
declare const _default: React.ComponentClass<TabControllerProps & {
29+
useCustomTheme?: boolean | undefined;
30+
}, any> & {
31+
TabBar: typeof TabBar;
32+
TabBarItem: typeof TabBarItem;
33+
TabPage: typeof TabPage;
34+
PageCarousel: typeof PageCarousel;
35+
};
36+
export default _default;

src/components/tabController/index.tsx

Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
// TODO: support commented props
22
import React, {Component} from 'react';
3-
import PropTypes from 'prop-types';
43
import _ from 'lodash';
54
import Reanimated, {Easing} from 'react-native-reanimated';
65
import {State} from 'react-native-gesture-handler';
76
import {timing, fract, between} from 'react-native-redash';
87
import {Constants} from '../../helpers';
9-
import {asBaseComponent} from '../../commons';
8+
import {asBaseComponent} from '../../commons/new';
109
import TabBarContext from './TabBarContext';
1110
import TabBar from './TabBar';
12-
import TabBarItem from './TabBarItem';
11+
import TabBarItem, {TabBarItemProps} from './TabBarItem';
1312
import TabPage from './TabPage';
1413
import PageCarousel from './PageCarousel';
1514

@@ -36,54 +35,80 @@ const {
3635
multiply
3736
} = Reanimated;
3837

38+
interface TabControllerProps {
39+
/**
40+
* The list of tab bar items
41+
*/
42+
items: TabBarItemProps[];
43+
/**
44+
* Initial selected index
45+
*/
46+
selectedIndex: number;
47+
/**
48+
* callback for when index has change (will not be called on ignored items)
49+
*/
50+
onChangeIndex: (index: number) => void;
51+
/**
52+
* When using TabController.PageCarousel this should be turned on
53+
*/
54+
asCarousel?: boolean;
55+
/**
56+
* Pass for custom carousel page width
57+
*/
58+
carouselPageWidth?: number;
59+
}
60+
61+
interface StateProps {
62+
selectedIndex: number;
63+
asCarousel?: boolean;
64+
pageWidth: number;
65+
// items
66+
items: TabControllerProps['items'];
67+
itemStates: any[]; // TODO: typescript?
68+
ignoredItems: any[]; // TODO: typescript?
69+
// animated values
70+
targetPage: any; // TODO: typescript?
71+
currentPage: any; // TODO: typescript?
72+
carouselOffset: any; // TODO: typescript?
73+
containerWidth: any; // TODO: typescript?
74+
// callbacks
75+
registerTabItems: (tabItemsCount: number, ignoredItems: StateProps['ignoredItems']) => void;
76+
onChangeIndex: (index: number) => void;
77+
}
78+
3979
/**
4080
* @description: A performant solution for a tab controller with lazy load mechanism
4181
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.js
4282
* @notes: This component is based on react-native-gesture-handler
4383
* @important: On Android, if using react-native-navigation, make sure to wrap your screen with gestureHandlerRootHOC
4484
* @importantLink: https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html#with-wix-react-native-navigation-https-githubcom-wix-react-native-navigation
4585
*/
46-
class TabController extends Component {
86+
class TabController extends Component<TabControllerProps, StateProps> {
4787
static displayName = 'TabController';
4888
static contextType = TabBarContext;
4989

50-
static propTypes = {
51-
/**
52-
* The list of tab bar items
53-
*/
54-
items: PropTypes.arrayOf(PropTypes.shape(TabBarItem.propTypes)),
55-
/**
56-
* Initial selected index
57-
*/
58-
selectedIndex: PropTypes.number,
59-
/**
60-
* callback for when index has change (will not be called on ignored items)
61-
*/
62-
onChangeIndex: PropTypes.func,
63-
/**
64-
* When using TabController.PageCarousel this should be turned on
65-
*/
66-
asCarousel: PropTypes.bool,
67-
/**
68-
* Pass for custom carousel page width
69-
*/
70-
carouselPageWidth: PropTypes.number
71-
};
90+
static TabBar: typeof TabBar;
91+
static TabBarItem: typeof TabBarItem;
92+
static TabPage: typeof TabPage;
93+
static PageCarousel: typeof PageCarousel;
7294

7395
static defaultProps = {
7496
selectedIndex: 0,
7597
activeOpacity: 0.2
7698
};
7799

78-
constructor(props) {
100+
constructor(props: TabControllerProps) {
79101
super(props);
80102

81-
let itemStates = [];
82-
let ignoredItems = [];
103+
let itemStates: any[] = []; // TODO: typescript?
104+
let ignoredItems: any[] = []; // TODO: typescript?
83105
if (props.items) {
84-
const itemsCount = _.chain(props.items).filter(item => !item.ignore).size().value();
106+
const itemsCount = _.chain(props.items)
107+
.filter(item => !item.ignore)
108+
.size()
109+
.value();
85110
itemStates = _.times(itemsCount, () => new Value(State.UNDETERMINED));
86-
ignoredItems = _.filter(props.items, item => item.ignore);
111+
ignoredItems = _.filter<TabBarItemProps[]>(props.items, (item: TabBarItemProps) => item.ignore);
87112
}
88113

89114
this.state = {
@@ -105,7 +130,7 @@ class TabController extends Component {
105130
};
106131
}
107132

108-
static getDerivedStateFromProps(nextProps, prevState) {
133+
static getDerivedStateFromProps(nextProps: TabControllerProps, prevState: StateProps) {
109134
if (!_.isUndefined(nextProps.carouselPageWidth) && nextProps.carouselPageWidth !== prevState.pageWidth) {
110135
return {
111136
pageWidth: nextProps.carouselPageWidth
@@ -114,7 +139,7 @@ class TabController extends Component {
114139
return null;
115140
}
116141

117-
componentDidUpdate(prevProps, prevState) {
142+
componentDidUpdate(_prevProps: TabControllerProps, prevState: StateProps) {
118143
if (prevState.pageWidth !== this.state.pageWidth) {
119144
this.state.containerWidth.setValue(this.state.pageWidth);
120145
}
@@ -124,12 +149,12 @@ class TabController extends Component {
124149
return this.props.carouselPageWidth || Constants.screenWidth;
125150
}
126151

127-
registerTabItems = (tabItemsCount, ignoredItems) => {
152+
registerTabItems = (tabItemsCount: number, ignoredItems: StateProps['ignoredItems']) => {
128153
const itemStates = _.times(tabItemsCount, () => new Value(State.UNDETERMINED));
129154
this.setState({itemStates, ignoredItems});
130155
};
131156

132-
onPageChange = ([index]) => {
157+
onPageChange = ([index]: readonly number[]) => {
133158
_.invoke(this.props, 'onChangeIndex', index);
134159
};
135160

@@ -147,40 +172,47 @@ class TabController extends Component {
147172
..._.map(itemStates, (state, index) => {
148173
const ignoredItem = _.includes(ignoredItems, index);
149174
return [
150-
onChange(state,
175+
onChange(
176+
state,
177+
// @ts-ignore TODO: typescript?
151178
cond(and(eq(state, State.END), !ignoredItem), [
152179
set(fromPage, toPage),
153180
set(toPage, index),
154181
set(targetPage, index)
155-
]))
182+
])
183+
)
156184
];
157185
}),
158186

159187
// Animate currentPage to its target
160188
cond(neq(currentPage, toPage), [
161189
set(isAnimating, 1),
162-
set(currentPage,
163-
timing({clock, from: fromPage, to: toPage, duration: 280, easing: Easing.bezier(0.34, 1.3, 0.64, 1)}))
190+
set(
191+
currentPage,
192+
timing({clock, from: fromPage, to: toPage, duration: 280, easing: Easing.bezier(0.34, 1.3, 0.64, 1)})
193+
)
164194
]),
165195
// Set isAnimating flag off
166196
cond(and(eq(isAnimating, 1), not(clockRunning(clock))), set(isAnimating, 0)),
167197

168198
/* Page change by Carousel scroll */
169199
onChange(carouselOffset, [
170200
set(isScrolling, lessThan(round(abs(diff(carouselOffset))), round(containerWidth))),
171-
cond(and(not(isAnimating)), [
172-
set(currentPage,
201+
cond(not(isAnimating), [
202+
set(
203+
currentPage,
173204
interpolate(round(carouselOffset), {
174-
inputRange: itemStates.map((v, i) => round(multiply(i, containerWidth))),
175-
outputRange: itemStates.map((v, i) => i)
176-
})),
205+
inputRange: itemStates.map((_v, i) => round(multiply(i, containerWidth))),
206+
outputRange: itemStates.map((_v, i) => i)
207+
})
208+
),
177209
set(toPage, currentPage)
178210
])
179211
]),
180212
// Update/Sync target page when scrolling is done
181213
cond(and(eq(isScrolling, 1), eq(floor(abs(diff(carouselOffset))), 0)), [
182214
set(isScrolling, 0),
183-
cond(not(between(fract(currentPage), 0.1, 0.9, 1)), set(targetPage, round(currentPage)))
215+
cond(not(between(fract(currentPage), 0.1, 0.9, true)), set(targetPage, round(currentPage)))
184216
]),
185217

186218
/* Invoke index change */
@@ -204,4 +236,13 @@ TabController.TabBar = TabBar;
204236
TabController.TabBarItem = TabBarItem;
205237
TabController.TabPage = TabPage;
206238
TabController.PageCarousel = PageCarousel;
207-
export default asBaseComponent(TabController);
239+
export default asBaseComponent<
240+
TabControllerProps,
241+
{
242+
TabBar: typeof TabBar;
243+
TabBarItem: typeof TabBarItem;
244+
TabPage: typeof TabPage;
245+
PageCarousel: typeof PageCarousel;
246+
}
247+
//@ts-ignore typescript - will be fixed when moved to functional component
248+
>(TabController);

0 commit comments

Comments
 (0)