chore: setup monorepo with yarn workspaces (#38)

This commit is contained in:
Satyajit Sahoo
2019-08-02 00:40:57 +02:00
committed by Michał Osadnik
parent 0d68f1ed59
commit ce7d163073
62 changed files with 7933 additions and 3020 deletions

View File

@@ -0,0 +1,4 @@
{
"f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true,
"89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true
}

1
packages/example/App.tsx Normal file
View File

@@ -0,0 +1 @@
export { default } from './src/index';

30
packages/example/app.json Normal file
View File

@@ -0,0 +1,30 @@
{
"expo": {
"name": "@navigation-ex/example",
"slug": "navigation-ex-example",
"privacy": "public",
"sdkVersion": "34.0.0",
"platforms": [
"ios",
"android",
"web"
],
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@@ -0,0 +1,9 @@
/* eslint-disable import/no-commonjs */
/* eslint-env node */
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};

View File

@@ -0,0 +1,46 @@
/* eslint-disable import/no-commonjs, import/no-extraneous-dependencies */
/* eslint-env node */
const path = require('path');
const fs = require('fs');
const escape = require('escape-string-regexp');
const blacklist = require('metro-config/src/defaults/blacklist');
module.exports = {
projectRoot: __dirname,
watchFolders: [path.resolve(__dirname, '..', '..')],
resolver: {
blacklistRE: blacklist(
[
...fs
.readdirSync(path.resolve(__dirname, '..'))
.filter(d => d !== 'example'),
'..',
].map(
it =>
new RegExp(
`^${escape(
path.resolve(__dirname, '..', it, 'node_modules')
)}\\/.*$`
)
)
),
providesModuleNodeModules: [
'@babel/runtime',
'react',
'react-native',
'shortid',
],
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
};

View File

@@ -0,0 +1,41 @@
{
"name": "@navigation-ex/example",
"version": "0.0.0",
"private": true,
"workspaces": {
"nohoist": [
"*",
"*/**"
]
},
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"expo": "^34.0.1",
"react": "16.8.3",
"react-dom": "^16.8.3",
"react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz",
"react-native-paper": "3.0.0-alpha.3",
"react-native-web": "^0.11.4",
"scheduler": "^0.14.0",
"shortid": "^2.2.14"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@expo/webpack-config": "^0.7.0",
"@types/react": "^16.8.23",
"@types/react-native": "^0.57.65",
"babel-preset-expo": "^6.0.0",
"expo-cli": "^3.0.6"
},
"resolutions": {
"react": "16.8.3",
"react-dom": "^16.8.3"
}
}

View File

@@ -0,0 +1,105 @@
/* eslint-disable react-native/no-inline-styles */
import * as React from 'react';
import { Text, View } from 'react-native';
import {
useNavigationBuilder,
NavigationProp,
ParamListBase,
createNavigator,
} from '@navigation-ex/core';
import {
StackRouter,
StackRouterOptions,
StackNavigationState,
} from '@navigation-ex/routers';
type Props = StackRouterOptions & {
children: React.ReactNode;
};
export type StackNavigationOptions = {
/**
* Title text for the screen.
*/
title?: string;
};
export type StackNavigationProp<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = string
> = NavigationProp<
ParamList,
RouteName,
StackNavigationState,
StackNavigationOptions
> & {
/**
* Push a new screen onto the stack.
*
* @param name Name of the route for the tab.
* @param [params] Params object for the route.
*/
push<RouteName extends keyof ParamList>(
...args: ParamList[RouteName] extends void
? [RouteName]
: [RouteName, ParamList[RouteName]]
): void;
/**
* Pop a screen from the stack.
*/
pop(count?: number): void;
/**
* Pop to the first route in the stack, dismissing all other screens.
*/
popToTop(): void;
};
export function StackNavigator(props: Props) {
const { state, descriptors } = useNavigationBuilder<
StackNavigationState,
StackNavigationOptions,
StackRouterOptions
>(StackRouter, props);
return (
<View>
{state.routes.map((route, i) => (
<View
key={route.key}
style={{
position: 'absolute',
margin: 20,
left: i * 20,
top: i * 20,
padding: 10,
height: 480,
width: 320,
backgroundColor: 'white',
borderRadius: 3,
}}
>
{descriptors[route.key].render()}
</View>
))}
<View
style={{
position: 'absolute',
left: 40,
width: 120,
padding: 10,
backgroundColor: 'tomato',
borderRadius: 3,
}}
>
<Text>{descriptors[state.routes[state.index].key].options.title}</Text>
</View>
</View>
);
}
export default createNavigator<StackNavigationOptions, typeof StackNavigator>(
StackNavigator
);

View File

@@ -0,0 +1,78 @@
/* eslint-disable react-native/no-inline-styles */
import * as React from 'react';
import { View } from 'react-native';
import {
useNavigationBuilder,
NavigationProp,
ParamListBase,
createNavigator,
} from '@navigation-ex/core';
import {
TabRouter,
TabRouterOptions,
TabNavigationState,
} from '@navigation-ex/routers';
type Props = TabRouterOptions & {
children: React.ReactNode;
};
export type TabNavigationOptions = {
/**
* Title text for the screen.
*/
title?: string;
};
export type TabNavigationProp<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = string
> = NavigationProp<
ParamList,
RouteName,
TabNavigationState,
TabNavigationOptions
> & {
/**
* Jump to an existing tab.
*
* @param name Name of the route for the tab.
* @param [params] Params object for the route.
*/
jumpTo<RouteName extends Extract<keyof ParamList, string>>(
...args: ParamList[RouteName] extends void
? [RouteName]
: [RouteName, ParamList[RouteName]]
): void;
};
export function TabNavigator(props: Props) {
const { state, descriptors } = useNavigationBuilder<
TabNavigationState,
TabNavigationOptions,
TabRouterOptions
>(TabRouter, props);
return (
<View style={{ display: 'flex', flexDirection: 'row', height: '100%' }}>
{state.routes.map((route, i, self) => (
<View
key={route.key}
style={{
width: `${100 / self.length}%`,
padding: 10,
borderRadius: 3,
backgroundColor: i === state.index ? 'tomato' : 'white',
}}
>
{descriptors[route.key].render()}
</View>
))}
</View>
);
}
export default createNavigator<TabNavigationOptions, typeof TabNavigator>(
TabNavigator
);

View File

@@ -0,0 +1,211 @@
import * as React from 'react';
import {
View,
Text,
Platform,
AsyncStorage,
YellowBox,
StyleSheet,
} from 'react-native';
import { Button } from 'react-native-paper';
import {
NavigationContainer,
CompositeNavigationProp,
NavigationHelpers,
RouteProp,
InitialState,
useFocusEffect,
} from '@navigation-ex/core';
import createStackNavigator, { StackNavigationProp } from './StackNavigator';
import createTabNavigator, { TabNavigationProp } from './TabNavigator';
type StackParamList = {
first: { author: string };
second: undefined;
third: undefined;
};
type TabParamList = {
fourth: undefined;
fifth: undefined;
};
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
const Stack = createStackNavigator<StackParamList>();
const Tab = createTabNavigator<TabParamList>();
const First = ({
navigation,
route,
}: {
navigation: CompositeNavigationProp<
StackNavigationProp<StackParamList, 'first'>,
NavigationHelpers<TabParamList>
>;
route: RouteProp<StackParamList, 'first'>;
}) => {
const updateTitle = React.useCallback(() => {
if (Platform.OS !== 'web') {
return;
}
document.title = `${route.name} (${route.params.author})`;
return () => {
document.title = '';
};
}, [route.name, route.params.author]);
useFocusEffect(updateTitle);
return (
<View>
<Text style={styles.title}>First, {route.params.author}</Text>
<Button onPress={() => navigation.push('second')}>Push second</Button>
<Button onPress={() => navigation.push('third')}>Push third</Button>
<Button onPress={() => navigation.navigate('fourth')}>
Navigate to fourth
</Button>
<Button onPress={() => navigation.navigate('first', { author: 'John' })}>
Navigate with params
</Button>
<Button onPress={() => navigation.pop()}>Pop</Button>
</View>
);
};
const Second = ({
navigation,
}: {
navigation: CompositeNavigationProp<
StackNavigationProp<StackParamList, 'second'>,
NavigationHelpers<TabParamList>
>;
}) => {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
const timer = setInterval(() => setCount(c => c + 1), 1000);
return () => clearInterval(timer);
}, []);
navigation.setOptions({
title: `Count ${count}`,
});
return (
<View>
<Text style={styles.title}>Second</Text>
<Button onPress={() => navigation.push('first', { author: 'Joel' })}>
Push first
</Button>
<Button onPress={() => navigation.pop()}>Pop</Button>
</View>
);
};
const Fourth = ({
navigation,
}: {
navigation: CompositeNavigationProp<
TabNavigationProp<TabParamList, 'fourth'>,
StackNavigationProp<StackParamList>
>;
}) => (
<View>
<Text style={styles.title}>Fourth</Text>
<Button onPress={() => navigation.jumpTo('fifth')}>Jump to fifth</Button>
<Button onPress={() => navigation.push('first', { author: 'Jake' })}>
Push first
</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
const Fifth = ({
navigation,
}: {
navigation: CompositeNavigationProp<
TabNavigationProp<TabParamList, 'fifth'>,
StackNavigationProp<StackParamList>
>;
}) => (
<View>
<Text style={styles.title}>Fifth</Text>
<Button onPress={() => navigation.jumpTo('fourth')}>Jump to fourth</Button>
<Button onPress={() => navigation.push('second')}>Push second</Button>
<Button onPress={() => navigation.pop()}>Pop</Button>
</View>
);
const PERSISTENCE_KEY = 'NAVIGATION_STATE';
export default function App() {
const [isReady, setIsReady] = React.useState(false);
const [initialState, setInitialState] = React.useState<
InitialState | undefined
>();
React.useEffect(() => {
AsyncStorage.getItem(PERSISTENCE_KEY).then(
data => {
try {
const result = JSON.parse(data || '');
if (result) {
setInitialState(result);
}
} finally {
setIsReady(true);
}
},
() => setIsReady(true)
);
}, []);
if (!isReady) {
return null;
}
return (
<NavigationContainer
initialState={initialState}
onStateChange={state =>
AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state))
}
>
<Stack.Navigator>
<Stack.Screen
name="first"
component={First}
options={({ route }) => ({
title: `Foo (${route.params ? route.params.author : ''})`,
})}
initialParams={{ author: 'Jane' }}
/>
<Stack.Screen name="second" options={{ title: 'Bar' }}>
{props => <Second {...props} />}
</Stack.Screen>
<Stack.Screen name="third" options={{ title: 'Baz' }}>
{() => (
<Tab.Navigator initialRouteName="fifth">
<Tab.Screen name="fourth" component={Fourth} />
<Tab.Screen name="fifth" component={Fifth} />
</Tab.Navigator>
)}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 8,
},
});

View File

@@ -0,0 +1,17 @@
/* eslint-disable import/no-commonjs */
/* eslint-env node */
const createExpoWebpackConfigAsync = require('@expo/webpack-config');
module.exports = async function(env, argv) {
const config = await createExpoWebpackConfigAsync(env, argv);
config.module.rules.push({
test: /@navigation-ex/,
use: 'babel-loader',
});
config.resolve.alias['react'] = require.resolve('react');
return config;
};