diff --git a/packages/tabs/.eslintrc b/packages/tabs/.eslintrc index 2c8e7681..d089fe6d 100644 --- a/packages/tabs/.eslintrc +++ b/packages/tabs/.eslintrc @@ -15,6 +15,8 @@ }, "rules": { - "import/named": "off" + "import/named": "off", + "import/default": "off", + "import/namespace": "off", } } diff --git a/packages/tabs/example/App.js b/packages/tabs/example/App.tsx similarity index 91% rename from packages/tabs/example/App.js rename to packages/tabs/example/App.tsx index 34a5c7de..b755bb92 100644 --- a/packages/tabs/example/App.js +++ b/packages/tabs/example/App.tsx @@ -4,6 +4,7 @@ import { View, TouchableOpacity, StyleSheet } from 'react-native'; import { Assets as StackAssets, createStackNavigator, + NavigationStackScreenProps, } from 'react-navigation-stack'; import { Themed, @@ -20,7 +21,7 @@ import MaterialTopTabs from './src/MaterialTopTabs'; // Load the back button etc Asset.loadAsync(StackAssets); -const Home = props => { +const Home = (props: NavigationStackScreenProps) => { let theme = useTheme(); return ( @@ -62,7 +63,7 @@ const List = createStackNavigator({ const Navigation = createAppContainer(List); const App = () => { - let [theme, setTheme] = React.useState('light'); + let [theme, setTheme] = React.useState<'light' | 'dark'>('light'); return ( @@ -97,7 +98,7 @@ const styles = { flex: 1, }, buttonContainer: { - position: 'absolute', + position: 'absolute' as const, bottom: 60, right: 20, }, @@ -111,8 +112,8 @@ const styles = { borderRadius: 25, width: 50, height: 50, - alignItems: 'center', - justifyContent: 'center', + alignItems: 'center' as const, + justifyContent: 'center' as const, elevation: 5, borderWidth: 1, }, @@ -130,4 +131,5 @@ const styles = { }, }; +// @ts-ignore registerRootComponent(App); diff --git a/packages/tabs/example/package.json b/packages/tabs/example/package.json index db329079..84ae9772 100644 --- a/packages/tabs/example/package.json +++ b/packages/tabs/example/package.json @@ -14,12 +14,12 @@ "expo-asset": "^6.0.0", "expo-constants": "~5.0.1", "react": "16.8.3", - "react-navigation": "^4.0.3", "react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz", "react-native-safe-area-view": "0.13.1", "react-native-screens": "1.0.0-alpha.22", "react-native-tab-view": "^2.10.0", - "react-navigation-stack": "1.5.3" + "react-navigation": "^4.0.4", + "react-navigation-stack": "^1.7.2" }, "devDependencies": { "babel-plugin-module-resolver": "^3.2.0", diff --git a/packages/tabs/example/src/BottomTabs.js b/packages/tabs/example/src/BottomTabs.tsx similarity index 82% rename from packages/tabs/example/src/BottomTabs.js rename to packages/tabs/example/src/BottomTabs.tsx index 7d712765..6e3f5b06 100644 --- a/packages/tabs/example/src/BottomTabs.js +++ b/packages/tabs/example/src/BottomTabs.tsx @@ -6,10 +6,16 @@ import Article from './Shared/Article'; import Chat from './Shared/Chat'; import Contacts from './Shared/Contacts'; -// eslint-disable-next-line import/default +// @ts-ignore import TouchableBounce from 'react-native/Libraries/Components/Touchable/TouchableBounce'; -const tabBarIcon = name => ({ tintColor, horizontal }) => ( +const tabBarIcon = (name: string) => ({ + tintColor, + horizontal, +}: { + tintColor: string; + horizontal: boolean; +}) => ( ); @@ -61,9 +67,14 @@ class ContactsScreen extends React.Component { } } -export default createBottomTabNavigator({ - AlbumsScreen, - ArticleScreen, - ChatScreen, - ContactsScreen, -}); +export default createBottomTabNavigator( + { + AlbumsScreen, + ArticleScreen, + ChatScreen, + ContactsScreen, + }, + { + initialRouteName: 'AlbumsScreen', + } +); diff --git a/packages/tabs/example/src/MaterialTopTabs.js b/packages/tabs/example/src/MaterialTopTabs.tsx similarity index 100% rename from packages/tabs/example/src/MaterialTopTabs.js rename to packages/tabs/example/src/MaterialTopTabs.tsx diff --git a/packages/tabs/example/src/Shared/Albums.js b/packages/tabs/example/src/Shared/Albums.tsx similarity index 93% rename from packages/tabs/example/src/Shared/Albums.js rename to packages/tabs/example/src/Shared/Albums.tsx index 7ba6099c..608d30a9 100644 --- a/packages/tabs/example/src/Shared/Albums.js +++ b/packages/tabs/example/src/Shared/Albums.tsx @@ -1,5 +1,3 @@ -/* @flow */ - import * as React from 'react'; import { Image, Dimensions, ScrollView, StyleSheet } from 'react-native'; @@ -14,7 +12,7 @@ const COVERS = [ require('../../assets/album-art-8.jpg'), ]; -export default class Albums extends React.Component<*> { +export default class Albums extends React.Component { render() { return ( { +export default class Article extends React.Component { render() { return ( { +export default class Albums extends React.Component { render() { return ( diff --git a/packages/tabs/example/src/Shared/Contacts.js b/packages/tabs/example/src/Shared/Contacts.tsx similarity index 93% rename from packages/tabs/example/src/Shared/Contacts.js rename to packages/tabs/example/src/Shared/Contacts.tsx index c8316681..81f6da35 100644 --- a/packages/tabs/example/src/Shared/Contacts.js +++ b/packages/tabs/example/src/Shared/Contacts.tsx @@ -1,9 +1,9 @@ -/* @flow */ - import * as React from 'react'; import { View, Text, StyleSheet, FlatList } from 'react-native'; -const CONTACTS = [ +type Item = { name: string; number: number }; + +const CONTACTS: Item[] = [ { name: 'Marissa Castillo', number: 7766398169 }, { name: 'Denzel Curry', number: 9394378449 }, { name: 'Miles Ferguson', number: 8966872888 }, @@ -57,7 +57,7 @@ const CONTACTS = [ ]; class ContactItem extends React.PureComponent<{ - item: { name: string, number: number }, + item: Item; }> { render() { const { item } = this.props; @@ -78,8 +78,8 @@ class ContactItem extends React.PureComponent<{ } } -export default class Contacts extends React.Component<*> { - _renderItem = ({ item }) => ; +export default class Contacts extends React.Component { + _renderItem = ({ item }: { item: Item }) => ; _ItemSeparator = () => ; @@ -87,7 +87,7 @@ export default class Contacts extends React.Component<*> { return ( String(i)} + keyExtractor={(_, i) => String(i)} renderItem={this._renderItem} ItemSeparatorComponent={this._ItemSeparator} /> diff --git a/packages/tabs/example/src/Shared/PhotoGrid.js b/packages/tabs/example/src/Shared/PhotoGrid.js deleted file mode 100644 index e91a0b7a..00000000 --- a/packages/tabs/example/src/Shared/PhotoGrid.js +++ /dev/null @@ -1,78 +0,0 @@ -import * as React from 'react'; -import { View, Image, ScrollView, Dimensions, StyleSheet } from 'react-native'; -import { withNavigation } from 'react-navigation'; - -@withNavigation -class NavigationAwareScrollView extends React.Component { - componentDidMount() { - this.props.navigation.addListener('willFocus', () => { - this._isFocused = true; - }); - - this.props.navigation.addListener('willBlur', () => { - this._isFocused = false; - }); - - this.props.navigation.addListener('refocus', () => { - if (this._isFocused) { - this._component.scrollTo({ x: 0, y: 0 }); - } - }); - } - - setNativeProps(props) { - this._component.setNativeProps(props); - } - - _setComponentRef(c) { - this._component = c; - } - - getNode() { - return this._component; - } - - render() { - return ( - { - this._component = view; - }} - /> - ); - } -} - -export default function PhotoGrid({ id }) { - const PHOTOS = Array.from({ length: 24 }).map( - (_, i) => `https://unsplash.it/300/300/?random&__id=${id}${i}` - ); - - return ( - - {PHOTOS.map(uri => ( - - - - ))} - - ); -} - -const styles = StyleSheet.create({ - content: { - flexDirection: 'row', - flexWrap: 'wrap', - padding: 4, - }, - item: { - height: Dimensions.get('window').width / 2, - width: '50%', - padding: 4, - }, - photo: { - flex: 1, - resizeMode: 'cover', - }, -}); diff --git a/packages/tabs/example/src/Shared/PhotoGrid.tsx b/packages/tabs/example/src/Shared/PhotoGrid.tsx new file mode 100644 index 00000000..8c188f39 --- /dev/null +++ b/packages/tabs/example/src/Shared/PhotoGrid.tsx @@ -0,0 +1,86 @@ +import * as React from 'react'; +import { + View, + Image, + ScrollView, + Dimensions, + StyleSheet, + StyleProp, + ViewStyle, + ScrollViewProperties, +} from 'react-native'; +import { + withNavigation, + NavigationScreenProp, + NavigationRoute, + NavigationEventSubscription, +} from 'react-navigation'; + +class NavigationAwareScrollViewBase extends React.Component<{ + navigation: NavigationScreenProp; + contentContainerStyle: StyleProp; +}> { + componentDidMount() { + this.subscription = this.props.navigation.addListener('refocus', () => { + if (this.props.navigation.isFocused()) { + this.root.current && this.root.current.scrollTo({ x: 0, y: 0 }); + } + }); + } + + componentWillUnmount() { + this.subscription && this.subscription.remove(); + } + + setNativeProps(props: ScrollViewProperties) { + // @ts-ignore + this.root.current.setNativeProps(props); + } + + getNode() { + return this.root.current; + } + + private subscription: NavigationEventSubscription | undefined; + + private root = React.createRef(); + + render() { + return ; + } +} + +const NavigationAwareScrollView = withNavigation(NavigationAwareScrollViewBase); + +export default function PhotoGrid({ id }: { id: string }) { + const PHOTOS = Array.from({ length: 24 }).map( + (_, i) => `https://unsplash.it/300/300/?random&__id=${id}${i}` + ); + + return ( + + {PHOTOS.map(uri => ( + + + + ))} + + ); +} + +const styles = StyleSheet.create({ + content: { + flexDirection: 'row', + flexWrap: 'wrap', + padding: 4, + }, + item: { + height: Dimensions.get('window').width / 2, + width: '50%', + padding: 4, + }, + photo: { + flex: 1, + resizeMode: 'cover', + }, +}); diff --git a/packages/tabs/example/yarn.lock b/packages/tabs/example/yarn.lock index 1ed87aa4..a1732745 100644 --- a/packages/tabs/example/yarn.lock +++ b/packages/tabs/example/yarn.lock @@ -4587,17 +4587,17 @@ react-native-webview@5.8.1: xmldoc "^0.4.0" yargs "^9.0.0" -react-navigation-stack@1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/react-navigation-stack/-/react-navigation-stack-1.5.3.tgz#cdc9f5a6dbdc55509a15f60d765722573dec1997" - integrity sha512-MQcwDVbZUYsTtDJb5cFOSm+K+e7KpUCoROaGoUOR+JHWE3uuaJ3pd/Nu+32a57J98TNBf4qq0+2TPJWl6z6IBg== +react-navigation-stack@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/react-navigation-stack/-/react-navigation-stack-1.7.2.tgz#d7cf7a7dc76c2390024be6358fc65461c848a034" + integrity sha512-72oL9rVXUFvFayoA7k+OgXcwP/6e5BAtCSpUXfKX+lZYrJe3BvuhZz2KDhEjdfbuP/sNok55ZRzc3/X1kh5mxQ== dependencies: prop-types "^15.7.2" -react-navigation@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-4.0.3.tgz#ba2cacb71db56e22ee50d774829ebc7fa95a0724" - integrity sha512-oASR5gHwd6se1Mw8AM4Ie8GicD5mKzRiYP6oaQujiQroQzQPij9sXxkRSqOscd/Kw1/Hf3htvBX3ZRPbOkWsfA== +react-navigation@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-4.0.4.tgz#afa43c7183891d38708cf57f1d4394fed1d4c2ad" + integrity sha512-MZeVkYkFTKZobhrXMV3Hgeg0HHeokCrYsbxActVfO0n6zfzm0/La6EiC2mIHiwOymvb1ZygyFf90vryLUMEBNA== dependencies: "@react-navigation/core" "^3.5.0" "@react-navigation/native" "^3.6.2" diff --git a/packages/tabs/package.json b/packages/tabs/package.json index ee1c5413..d3cb7754 100644 --- a/packages/tabs/package.json +++ b/packages/tabs/package.json @@ -74,18 +74,18 @@ "react-native": "~0.57.1", "react-native-gesture-handler": "^1.4.1", "react-native-reanimated": "^1.2.0", - "react-navigation": "^4.0.3", + "react-navigation": "^4.0.4", "react-test-renderer": "16.5.0", "release-it": "^10.3.1", "typescript": "^3.5.2" }, "peerDependencies": { "react": "*", - "react-navigation": "^4.0.3", "react-native": "*", "react-native-gesture-handler": "^1.0.0", "react-native-reanimated": "^1.0.0-alpha", - "react-native-screens": "^1.0.0 || ^1.0.0-alpha" + "react-native-screens": "^1.0.0 || ^1.0.0-alpha", + "react-navigation": "^4.0.4" }, "husky": { "hooks": { diff --git a/packages/tabs/src/index.tsx b/packages/tabs/src/index.tsx index e7da8c5b..e1f29904 100644 --- a/packages/tabs/src/index.tsx +++ b/packages/tabs/src/index.tsx @@ -22,10 +22,12 @@ export { default as createTabNavigator } from './utils/createTabNavigator'; /** * Types */ - export { NavigationTabState, NavigationTabProp, + NavigationTabScreenProps, NavigationBottomTabOptions, NavigationMaterialTabOptions, + NavigationBottomTabScreenComponent, + NavigationMaterialTabScreenComponent, } from './types'; diff --git a/packages/tabs/src/types.tsx b/packages/tabs/src/types.tsx index 10fbb511..a4d3c04b 100644 --- a/packages/tabs/src/types.tsx +++ b/packages/tabs/src/types.tsx @@ -14,6 +14,8 @@ import { NavigationScreenProp, NavigationParams, NavigationDescriptor, + NavigationScreenConfig, + SupportedThemes, } from 'react-navigation'; export type NavigationTabState = NavigationState; @@ -178,6 +180,35 @@ export type NavigationMaterialTabOptions = NavigationCommonTabOptions & { swipeEnabled?: boolean | ((state: NavigationState) => boolean); }; +export type NavigationTabScreenProps< + Params = NavigationParams, + ScreenProps = unknown +> = { + theme: SupportedThemes; + navigation: NavigationTabProp; + screenProps: ScreenProps; +}; + +export type NavigationMaterialTabScreenComponent< + Params = NavigationParams, + ScreenProps = unknown +> = React.ComponentType> & { + navigationOptions?: NavigationScreenConfig< + NavigationMaterialTabOptions, + NavigationTabProp + >; +}; + +export type NavigationBottomTabScreenComponent< + Params = NavigationParams, + ScreenProps = unknown +> = React.ComponentType> & { + navigationOptions?: NavigationScreenConfig< + NavigationBottomTabOptions, + NavigationTabProp + >; +}; + export type SceneDescriptorMap = { [key: string]: NavigationDescriptor< NavigationParams, diff --git a/packages/tabs/src/utils/createTabNavigator.tsx b/packages/tabs/src/utils/createTabNavigator.tsx index ceb71377..6406cf4f 100644 --- a/packages/tabs/src/utils/createTabNavigator.tsx +++ b/packages/tabs/src/utils/createTabNavigator.tsx @@ -58,7 +58,12 @@ export default function createTabNavigator< TabView: React.ComponentType ): ( routes: RouteConfig, - config: Options + config?: CreateNavigatorConfig< + {}, + NavigationTabRouterConfig, + Partial, + NavigationTabProp + > ) => React.ComponentType< Pick> & ExtraProps > { @@ -256,7 +261,8 @@ export default function createTabNavigator< config: CreateNavigatorConfig< {}, NavigationTabRouterConfig, - Partial + Partial, + NavigationTabProp > = {} ) => { const router = TabRouter(routes, config as any); diff --git a/packages/tabs/tsconfig.json b/packages/tabs/tsconfig.json index 118759f0..d805f241 100644 --- a/packages/tabs/tsconfig.json +++ b/packages/tabs/tsconfig.json @@ -1,5 +1,9 @@ { "compilerOptions": { + "baseUrl": ".", + "paths": { + "react-navigation-tabs": ["./src/index"] + }, "allowUnreachableCode": false, "allowUnusedLabels": false, "esModuleInterop": true, @@ -15,6 +19,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "resolveJsonModule": true, + "skipLibCheck": true, "strict": true, "target": "esnext" } diff --git a/packages/tabs/yarn.lock b/packages/tabs/yarn.lock index 25a8fd2c..3625a062 100644 --- a/packages/tabs/yarn.lock +++ b/packages/tabs/yarn.lock @@ -7672,10 +7672,10 @@ react-native@~0.57.1: xmldoc "^0.4.0" yargs "^9.0.0" -react-navigation@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-4.0.3.tgz#ba2cacb71db56e22ee50d774829ebc7fa95a0724" - integrity sha512-oASR5gHwd6se1Mw8AM4Ie8GicD5mKzRiYP6oaQujiQroQzQPij9sXxkRSqOscd/Kw1/Hf3htvBX3ZRPbOkWsfA== +react-navigation@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-4.0.4.tgz#afa43c7183891d38708cf57f1d4394fed1d4c2ad" + integrity sha512-MZeVkYkFTKZobhrXMV3Hgeg0HHeokCrYsbxActVfO0n6zfzm0/La6EiC2mIHiwOymvb1ZygyFf90vryLUMEBNA== dependencies: "@react-navigation/core" "^3.5.0" "@react-navigation/native" "^3.6.2"