expo-template-default 51.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -0
- package/app/(tabs)/_layout.tsx +44 -0
- package/app/(tabs)/explore.tsx +111 -0
- package/app/(tabs)/index.tsx +72 -0
- package/app/+html.tsx +42 -0
- package/app/+not-found.tsx +32 -0
- package/app/_layout.tsx +40 -0
- package/app.json +34 -0
- package/assets/fonts/SpaceMono-Regular.ttf +0 -0
- package/assets/images/adaptive-icon.png +0 -0
- package/assets/images/favicon.png +0 -0
- package/assets/images/icon.png +0 -0
- package/assets/images/partial-react-logo.png +0 -0
- package/assets/images/react-logo.png +0 -0
- package/assets/images/react-logo@2x.png +0 -0
- package/assets/images/react-logo@3x.png +0 -0
- package/assets/images/splash.png +0 -0
- package/babel.config.js +6 -0
- package/components/Drawer.tsx +54 -0
- package/components/ExternalLink.tsx +25 -0
- package/components/HelloWave.tsx +42 -0
- package/components/ParallaxScrollView.tsx +81 -0
- package/components/ThemedText.tsx +60 -0
- package/components/ThemedView.tsx +18 -0
- package/components/__tests__/ThemedText-test.tsx +10 -0
- package/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +24 -0
- package/components/navigation/TabBarIcon.tsx +11 -0
- package/constants/Colors.ts +26 -0
- package/hooks/useColorScheme.ts +1 -0
- package/hooks/useColorScheme.web.ts +8 -0
- package/hooks/useThemeColor.ts +21 -0
- package/package.json +47 -0
- package/scripts/reset-project.js +72 -0
- package/tsconfig.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Welcome to your Expo app 👋
|
|
2
|
+
|
|
3
|
+
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
|
|
4
|
+
|
|
5
|
+
## Get started
|
|
6
|
+
|
|
7
|
+
1. Install dependencies
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. Start the app
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx expo start
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
In the output, you'll find options to open the app in a
|
|
20
|
+
|
|
21
|
+
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
|
|
22
|
+
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
|
|
23
|
+
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
|
|
24
|
+
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
|
|
25
|
+
|
|
26
|
+
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
|
|
27
|
+
|
|
28
|
+
## Get a fresh project
|
|
29
|
+
|
|
30
|
+
When you're ready, run:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm run reset-project
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
|
|
37
|
+
|
|
38
|
+
## Learn more
|
|
39
|
+
|
|
40
|
+
To learn more about developing your project with Expo, look at the following resources:
|
|
41
|
+
|
|
42
|
+
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
|
|
43
|
+
- [Learn Expo tutorial](https://docs.expo.dev/learn): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
|
|
44
|
+
|
|
45
|
+
## Join the community
|
|
46
|
+
|
|
47
|
+
Join our community of developers creating universal apps.
|
|
48
|
+
|
|
49
|
+
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
|
|
50
|
+
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Tabs } from "expo-router";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
import { TabBarIcon } from "@/components/navigation/TabBarIcon";
|
|
5
|
+
import { Colors } from "@/constants/Colors";
|
|
6
|
+
import { useColorScheme } from "@/hooks/useColorScheme";
|
|
7
|
+
|
|
8
|
+
export default function TabLayout() {
|
|
9
|
+
const colorScheme = useColorScheme();
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<Tabs
|
|
13
|
+
screenOptions={{
|
|
14
|
+
tabBarActiveTintColor: Colors[colorScheme ?? "light"].tint,
|
|
15
|
+
headerShown: false,
|
|
16
|
+
}}
|
|
17
|
+
>
|
|
18
|
+
<Tabs.Screen
|
|
19
|
+
name="index"
|
|
20
|
+
options={{
|
|
21
|
+
title: "Home",
|
|
22
|
+
tabBarIcon: ({ color, focused }) => (
|
|
23
|
+
<TabBarIcon
|
|
24
|
+
name={focused ? "home" : "home-outline"}
|
|
25
|
+
color={color}
|
|
26
|
+
/>
|
|
27
|
+
),
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
30
|
+
<Tabs.Screen
|
|
31
|
+
name="explore"
|
|
32
|
+
options={{
|
|
33
|
+
title: "Explore",
|
|
34
|
+
tabBarIcon: ({ color, focused }) => (
|
|
35
|
+
<TabBarIcon
|
|
36
|
+
name={focused ? "code-slash" : "code-slash-outline"}
|
|
37
|
+
color={color}
|
|
38
|
+
/>
|
|
39
|
+
),
|
|
40
|
+
}}
|
|
41
|
+
/>
|
|
42
|
+
</Tabs>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import Ionicons from "@expo/vector-icons/Ionicons";
|
|
2
|
+
import { StyleSheet, Image, Platform } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { Drawer } from "@/components/Drawer";
|
|
5
|
+
import { ExternalLink } from "@/components/ExternalLink";
|
|
6
|
+
import ParallaxScrollView from "@/components/ParallaxScrollView";
|
|
7
|
+
import { ThemedText } from "@/components/ThemedText";
|
|
8
|
+
import { ThemedView } from "@/components/ThemedView";
|
|
9
|
+
|
|
10
|
+
export default function TabTwoScreen() {
|
|
11
|
+
return (
|
|
12
|
+
<ParallaxScrollView
|
|
13
|
+
headerBackgroundColor={{ light: "#D0D0D0", dark: "#353636" }}
|
|
14
|
+
headerImage={
|
|
15
|
+
<Ionicons size={310} name="code-slash" style={styles.headerImage} />
|
|
16
|
+
}
|
|
17
|
+
>
|
|
18
|
+
<ThemedView style={styles.titleContainer}>
|
|
19
|
+
<ThemedText type="title">Explore</ThemedText>
|
|
20
|
+
</ThemedView>
|
|
21
|
+
<ThemedText>
|
|
22
|
+
This app includes example code to help you get started.
|
|
23
|
+
</ThemedText>
|
|
24
|
+
<Drawer title="File-based routing">
|
|
25
|
+
<ThemedText>
|
|
26
|
+
This app has two screens:{" "}
|
|
27
|
+
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText>{" "}
|
|
28
|
+
and{" "}
|
|
29
|
+
<ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
|
|
30
|
+
</ThemedText>
|
|
31
|
+
<ThemedText>
|
|
32
|
+
The layout file in{" "}
|
|
33
|
+
<ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{" "}
|
|
34
|
+
sets up the tab navigator.
|
|
35
|
+
</ThemedText>
|
|
36
|
+
<ExternalLink href="https://docs.expo.dev/router/introduction">
|
|
37
|
+
<ThemedText type="link">Learn more</ThemedText>
|
|
38
|
+
</ExternalLink>
|
|
39
|
+
</Drawer>
|
|
40
|
+
<Drawer title="Android, iOS, and web support">
|
|
41
|
+
<ThemedText>
|
|
42
|
+
You can open this project on Android, iOS, and the web. To open the web version, press <ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project.
|
|
43
|
+
</ThemedText>
|
|
44
|
+
</Drawer>
|
|
45
|
+
<Drawer title="Images">
|
|
46
|
+
<ThemedText>
|
|
47
|
+
For static images, you can use the{" "}
|
|
48
|
+
<ThemedText type="defaultSemiBold">@2x</ThemedText> and{" "}
|
|
49
|
+
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to
|
|
50
|
+
provide files for different screen densities
|
|
51
|
+
</ThemedText>
|
|
52
|
+
<Image
|
|
53
|
+
source={require("@/assets/images/react-logo.png")}
|
|
54
|
+
style={{ alignSelf: "center" }}
|
|
55
|
+
/>
|
|
56
|
+
<ExternalLink href="https://reactnative.dev/docs/images">
|
|
57
|
+
<ThemedText type="link">Learn more</ThemedText>
|
|
58
|
+
</ExternalLink>
|
|
59
|
+
</Drawer>
|
|
60
|
+
<Drawer title="Custom fonts">
|
|
61
|
+
<ThemedText>
|
|
62
|
+
Open <ThemedText type="defaultSemiBold">app/_layout.tsx</ThemedText>{" "}
|
|
63
|
+
to see how to load{" "}
|
|
64
|
+
<ThemedText style={{ fontFamily: "SpaceMono" }}>
|
|
65
|
+
custom fonts such as this one.
|
|
66
|
+
</ThemedText>
|
|
67
|
+
</ThemedText>
|
|
68
|
+
<ExternalLink href="https://docs.expo.dev/versions/latest/sdk/font">
|
|
69
|
+
<ThemedText type="link">Learn more</ThemedText>
|
|
70
|
+
</ExternalLink>
|
|
71
|
+
</Drawer>
|
|
72
|
+
<Drawer title="Light and dark mode components">
|
|
73
|
+
<ThemedText>
|
|
74
|
+
This template has light and dark mode support. The{" "}
|
|
75
|
+
<ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook
|
|
76
|
+
lets you inspect what the user's current color scheme is, and so you
|
|
77
|
+
can adjust UI colors accordingly.
|
|
78
|
+
</ThemedText>
|
|
79
|
+
<ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
|
|
80
|
+
<ThemedText type="link">Learn more</ThemedText>
|
|
81
|
+
</ExternalLink>
|
|
82
|
+
</Drawer>
|
|
83
|
+
<Drawer title="Animations">
|
|
84
|
+
<ThemedText>
|
|
85
|
+
This template includes an example of an animated component. The{" "}
|
|
86
|
+
<ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component
|
|
87
|
+
uses the <ThemedText type="defaultSemiBold">Animated</ThemedText> API
|
|
88
|
+
to create a waving hand animation.
|
|
89
|
+
</ThemedText>
|
|
90
|
+
{Platform.select({ ios: (<ThemedText>
|
|
91
|
+
The{" "}
|
|
92
|
+
<ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{" "}
|
|
93
|
+
component provides a parallax effect for the header image.
|
|
94
|
+
</ThemedText>) }) }
|
|
95
|
+
</Drawer>
|
|
96
|
+
</ParallaxScrollView>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const styles = StyleSheet.create({
|
|
101
|
+
headerImage: {
|
|
102
|
+
color: "#808080",
|
|
103
|
+
bottom: -90,
|
|
104
|
+
left: -35,
|
|
105
|
+
position: "absolute",
|
|
106
|
+
},
|
|
107
|
+
titleContainer: {
|
|
108
|
+
flexDirection: "row",
|
|
109
|
+
gap: 8,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Image, StyleSheet, Platform } from "react-native";
|
|
2
|
+
|
|
3
|
+
import ParallaxScrollView from "@/components/ParallaxScrollView";
|
|
4
|
+
import { ThemedText } from "@/components/ThemedText";
|
|
5
|
+
import { ThemedView } from "@/components/ThemedView";
|
|
6
|
+
import { HelloWave } from "@/components/HelloWave";
|
|
7
|
+
|
|
8
|
+
export default function HomeScreen() {
|
|
9
|
+
return (
|
|
10
|
+
<ParallaxScrollView
|
|
11
|
+
headerBackgroundColor={{ light: "#A1CEDC", dark: "#1D3D47" }}
|
|
12
|
+
headerImage={
|
|
13
|
+
<Image
|
|
14
|
+
source={require("@/assets/images/partial-react-logo.png")}
|
|
15
|
+
style={styles.reactLogo}
|
|
16
|
+
/>
|
|
17
|
+
}
|
|
18
|
+
>
|
|
19
|
+
<ThemedView style={styles.titleContainer}>
|
|
20
|
+
<ThemedText type="title">Welcome!</ThemedText>
|
|
21
|
+
<HelloWave />
|
|
22
|
+
</ThemedView>
|
|
23
|
+
<ThemedView style={styles.stepContainer}>
|
|
24
|
+
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
|
|
25
|
+
<ThemedText>
|
|
26
|
+
Edit{" "}
|
|
27
|
+
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText>{" "}
|
|
28
|
+
to see changes. Press{" "}
|
|
29
|
+
<ThemedText type="defaultSemiBold">
|
|
30
|
+
{Platform.select({ ios: "cmd + d", android: "cmd + m" })}</ThemedText> to open
|
|
31
|
+
developer tools.
|
|
32
|
+
</ThemedText>
|
|
33
|
+
</ThemedView>
|
|
34
|
+
<ThemedView style={styles.stepContainer}>
|
|
35
|
+
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
|
|
36
|
+
<ThemedText>
|
|
37
|
+
Tap the Explore tab to learn more about what's included in this
|
|
38
|
+
starter app.
|
|
39
|
+
</ThemedText>
|
|
40
|
+
</ThemedView>
|
|
41
|
+
<ThemedView style={styles.stepContainer}>
|
|
42
|
+
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
|
|
43
|
+
<ThemedText>
|
|
44
|
+
When you're ready, run{" "}
|
|
45
|
+
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText>{" "}
|
|
46
|
+
to get a fresh <ThemedText type="defaultSemiBold">app</ThemedText>{" "}
|
|
47
|
+
directory. This will move the current{" "}
|
|
48
|
+
<ThemedText type="defaultSemiBold">app</ThemedText> to{" "}
|
|
49
|
+
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
|
|
50
|
+
</ThemedText>
|
|
51
|
+
</ThemedView>
|
|
52
|
+
</ParallaxScrollView>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const styles = StyleSheet.create({
|
|
57
|
+
titleContainer: {
|
|
58
|
+
flexDirection: "row",
|
|
59
|
+
alignItems: "center",
|
|
60
|
+
gap: 8,
|
|
61
|
+
},
|
|
62
|
+
stepContainer: {
|
|
63
|
+
gap: 8,
|
|
64
|
+
},
|
|
65
|
+
reactLogo: {
|
|
66
|
+
height: 178,
|
|
67
|
+
width: 290,
|
|
68
|
+
bottom:0,
|
|
69
|
+
left: 0,
|
|
70
|
+
position: "absolute",
|
|
71
|
+
},
|
|
72
|
+
});
|
package/app/+html.tsx
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ScrollViewStyleReset } from "expo-router/html";
|
|
2
|
+
import { type PropsWithChildren } from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This file is web-only and used to configure the root HTML for every web page during static rendering.
|
|
6
|
+
* The contents of this function only run in Node.js environments and do not have access to the DOM or browser APIs.
|
|
7
|
+
*/
|
|
8
|
+
export default function Root({ children }: PropsWithChildren) {
|
|
9
|
+
return (
|
|
10
|
+
<html lang="en">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charSet="utf-8" />
|
|
13
|
+
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
|
14
|
+
<meta
|
|
15
|
+
name="viewport"
|
|
16
|
+
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
{/*
|
|
20
|
+
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
|
|
21
|
+
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
|
|
22
|
+
*/}
|
|
23
|
+
<ScrollViewStyleReset />
|
|
24
|
+
|
|
25
|
+
{/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
|
|
26
|
+
<style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
|
|
27
|
+
{/* Add any additional <head> elements that you want globally available on web... */}
|
|
28
|
+
</head>
|
|
29
|
+
<body>{children}</body>
|
|
30
|
+
</html>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const responsiveBackground = `
|
|
35
|
+
body {
|
|
36
|
+
background-color: #fff;
|
|
37
|
+
}
|
|
38
|
+
@media (prefers-color-scheme: dark) {
|
|
39
|
+
body {
|
|
40
|
+
background-color: #000;
|
|
41
|
+
}
|
|
42
|
+
}`;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Link, Stack } from "expo-router";
|
|
2
|
+
import { StyleSheet } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { ThemedText } from "@/components/ThemedText";
|
|
5
|
+
import { ThemedView } from "@/components/ThemedView";
|
|
6
|
+
|
|
7
|
+
export default function NotFoundScreen() {
|
|
8
|
+
return (
|
|
9
|
+
<>
|
|
10
|
+
<Stack.Screen options={{ title: "Oops!" }} />
|
|
11
|
+
<ThemedView style={styles.container}>
|
|
12
|
+
<ThemedText type="title">This screen doesn't exist.</ThemedText>
|
|
13
|
+
<Link href="/" style={styles.link}>
|
|
14
|
+
<ThemedText type="link">Go to home screen!</ThemedText>
|
|
15
|
+
</Link>
|
|
16
|
+
</ThemedView>
|
|
17
|
+
</>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const styles = StyleSheet.create({
|
|
22
|
+
container: {
|
|
23
|
+
flex: 1,
|
|
24
|
+
alignItems: "center",
|
|
25
|
+
justifyContent: "center",
|
|
26
|
+
padding: 20,
|
|
27
|
+
},
|
|
28
|
+
link: {
|
|
29
|
+
marginTop: 15,
|
|
30
|
+
paddingVertical: 15,
|
|
31
|
+
},
|
|
32
|
+
});
|
package/app/_layout.tsx
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DarkTheme,
|
|
3
|
+
DefaultTheme,
|
|
4
|
+
ThemeProvider,
|
|
5
|
+
} from "@react-navigation/native";
|
|
6
|
+
import { useFonts } from "expo-font";
|
|
7
|
+
import { Stack } from "expo-router";
|
|
8
|
+
import * as SplashScreen from "expo-splash-screen";
|
|
9
|
+
import { useEffect } from "react";
|
|
10
|
+
|
|
11
|
+
import { useColorScheme } from "@/hooks/useColorScheme";
|
|
12
|
+
|
|
13
|
+
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
|
14
|
+
SplashScreen.preventAutoHideAsync();
|
|
15
|
+
|
|
16
|
+
export default function RootLayout() {
|
|
17
|
+
const colorScheme = useColorScheme();
|
|
18
|
+
const [loaded] = useFonts({
|
|
19
|
+
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (loaded) {
|
|
24
|
+
SplashScreen.hideAsync();
|
|
25
|
+
}
|
|
26
|
+
}, [loaded]);
|
|
27
|
+
|
|
28
|
+
if (!loaded) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
|
|
34
|
+
<Stack>
|
|
35
|
+
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
|
36
|
+
<Stack.Screen name="+not-found" />
|
|
37
|
+
</Stack>
|
|
38
|
+
</ThemeProvider>
|
|
39
|
+
);
|
|
40
|
+
}
|
package/app.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"expo": {
|
|
3
|
+
"name": "Hello Expo",
|
|
4
|
+
"slug": "expo-template-default",
|
|
5
|
+
"scheme": "expo-template-default",
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"orientation": "portrait",
|
|
8
|
+
"icon": "./assets/images/icon.png",
|
|
9
|
+
"userInterfaceStyle": "automatic",
|
|
10
|
+
"splash": {
|
|
11
|
+
"image": "./assets/images/splash.png",
|
|
12
|
+
"resizeMode": "contain",
|
|
13
|
+
"backgroundColor": "#ffffff"
|
|
14
|
+
},
|
|
15
|
+
"ios": {
|
|
16
|
+
"supportsTablet": true
|
|
17
|
+
},
|
|
18
|
+
"android": {
|
|
19
|
+
"adaptiveIcon": {
|
|
20
|
+
"foregroundImage": "./assets/images/adaptive-icon.png",
|
|
21
|
+
"backgroundColor": "#ffffff"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"web": {
|
|
25
|
+
"bundler": "metro",
|
|
26
|
+
"output": "static",
|
|
27
|
+
"favicon": "./assets/images/favicon.png"
|
|
28
|
+
},
|
|
29
|
+
"plugins": ["expo-router"],
|
|
30
|
+
"experiments": {
|
|
31
|
+
"typedRoutes": true
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/babel.config.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
2
|
+
import { PropsWithChildren, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
LayoutAnimation,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
useColorScheme,
|
|
8
|
+
} from "react-native";
|
|
9
|
+
|
|
10
|
+
import { ThemedText } from "@/components/ThemedText";
|
|
11
|
+
import { ThemedView } from "@/components/ThemedView";
|
|
12
|
+
import { Colors } from "@/constants/Colors";
|
|
13
|
+
|
|
14
|
+
export function Drawer({
|
|
15
|
+
children,
|
|
16
|
+
title,
|
|
17
|
+
}: PropsWithChildren & { title: string }) {
|
|
18
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
19
|
+
const theme = useColorScheme() ?? "light";
|
|
20
|
+
|
|
21
|
+
const toggleOpen = () => {
|
|
22
|
+
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
|
23
|
+
setIsOpen((value) => !value);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<ThemedView>
|
|
28
|
+
<TouchableOpacity
|
|
29
|
+
style={styles.heading}
|
|
30
|
+
onPress={toggleOpen}
|
|
31
|
+
activeOpacity={0.8}
|
|
32
|
+
>
|
|
33
|
+
<Ionicons
|
|
34
|
+
name={isOpen ? "chevron-down" : "chevron-forward-outline"}
|
|
35
|
+
size={18}
|
|
36
|
+
color={theme === "light" ? Colors.light.icon : Colors.dark.icon}
|
|
37
|
+
/>
|
|
38
|
+
<ThemedText type="defaultSemiBold">{title}</ThemedText>
|
|
39
|
+
</TouchableOpacity>
|
|
40
|
+
<ThemedView style={styles.content}>{isOpen ? children : null}</ThemedView>
|
|
41
|
+
</ThemedView>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
heading: {
|
|
47
|
+
flexDirection: "row",
|
|
48
|
+
alignItems: "center",
|
|
49
|
+
gap: 4,
|
|
50
|
+
},
|
|
51
|
+
content: {
|
|
52
|
+
marginLeft: 24,
|
|
53
|
+
}
|
|
54
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Link } from "expo-router";
|
|
2
|
+
import * as WebBrowser from "expo-web-browser";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Platform } from "react-native";
|
|
5
|
+
|
|
6
|
+
export function ExternalLink(
|
|
7
|
+
props: Omit<React.ComponentProps<typeof Link>, "href"> & { href: string },
|
|
8
|
+
) {
|
|
9
|
+
return (
|
|
10
|
+
<Link
|
|
11
|
+
target="_blank"
|
|
12
|
+
{...props}
|
|
13
|
+
// @ts-expect-error: External URLs are not typed.
|
|
14
|
+
href={props.href}
|
|
15
|
+
onPress={(e) => {
|
|
16
|
+
if (Platform.OS !== "web") {
|
|
17
|
+
// Prevent the default behavior of linking to the default browser on native.
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
// Open the link in an in-app browser.
|
|
20
|
+
WebBrowser.openBrowserAsync(props.href as string);
|
|
21
|
+
}
|
|
22
|
+
}}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { Animated, StyleSheet } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { ThemedText } from "@/components/ThemedText";
|
|
5
|
+
|
|
6
|
+
export function HelloWave() {
|
|
7
|
+
const rotateAnimation = useRef(new Animated.Value(0)).current;
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
Animated.loop(Animated.sequence([
|
|
11
|
+
Animated.timing(rotateAnimation, {
|
|
12
|
+
toValue: 1,
|
|
13
|
+
duration: 150,
|
|
14
|
+
useNativeDriver: true,
|
|
15
|
+
}),
|
|
16
|
+
Animated.timing(rotateAnimation, {
|
|
17
|
+
toValue: 0,
|
|
18
|
+
duration: 150,
|
|
19
|
+
useNativeDriver: true,
|
|
20
|
+
}),
|
|
21
|
+
]), { iterations: 4 }).start();
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
const spin = rotateAnimation.interpolate({
|
|
25
|
+
inputRange: [0, 1],
|
|
26
|
+
outputRange: ['0deg', '25deg']
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Animated.View style={{ transform: [{ rotate: spin }] }}>
|
|
31
|
+
<ThemedText style={styles.text}>👋</ThemedText>
|
|
32
|
+
</Animated.View>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const styles = StyleSheet.create({
|
|
37
|
+
text: {
|
|
38
|
+
fontSize: 28,
|
|
39
|
+
lineHeight: 32,
|
|
40
|
+
marginTop: -6
|
|
41
|
+
},
|
|
42
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { PropsWithChildren } from "react";
|
|
2
|
+
import { StyleSheet, useColorScheme } from "react-native";
|
|
3
|
+
import Animated, {
|
|
4
|
+
interpolate,
|
|
5
|
+
useAnimatedRef,
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
useScrollViewOffset,
|
|
8
|
+
} from "react-native-reanimated";
|
|
9
|
+
|
|
10
|
+
import { ThemedView } from "@/components/ThemedView";
|
|
11
|
+
|
|
12
|
+
const HEADER_HEIGHT = 250;
|
|
13
|
+
|
|
14
|
+
export default function ParallaxScrollView({
|
|
15
|
+
children,
|
|
16
|
+
headerImage,
|
|
17
|
+
headerBackgroundColor,
|
|
18
|
+
}: PropsWithChildren<{
|
|
19
|
+
headerImage: React.ReactElement;
|
|
20
|
+
headerBackgroundColor: { dark: string; light: string };
|
|
21
|
+
}>) {
|
|
22
|
+
const colorScheme = useColorScheme() || "light";
|
|
23
|
+
const scrollRef = useAnimatedRef<Animated.ScrollView>();
|
|
24
|
+
const scrollOffset = useScrollViewOffset(scrollRef);
|
|
25
|
+
|
|
26
|
+
const headerAnimatedStyle = useAnimatedStyle(() => {
|
|
27
|
+
return {
|
|
28
|
+
transform: [
|
|
29
|
+
{
|
|
30
|
+
translateY: interpolate(
|
|
31
|
+
scrollOffset.value,
|
|
32
|
+
[-HEADER_HEIGHT, 0, HEADER_HEIGHT],
|
|
33
|
+
[-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75],
|
|
34
|
+
),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
scale: interpolate(
|
|
38
|
+
scrollOffset.value,
|
|
39
|
+
[-HEADER_HEIGHT, 0, HEADER_HEIGHT],
|
|
40
|
+
[2, 1, 1],
|
|
41
|
+
),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<ThemedView style={styles.container}>
|
|
49
|
+
<Animated.ScrollView ref={scrollRef} scrollEventThrottle={16}>
|
|
50
|
+
<Animated.View
|
|
51
|
+
style={[
|
|
52
|
+
styles.header,
|
|
53
|
+
{ backgroundColor: headerBackgroundColor[colorScheme] },
|
|
54
|
+
headerAnimatedStyle,
|
|
55
|
+
]}
|
|
56
|
+
>
|
|
57
|
+
{headerImage}
|
|
58
|
+
</Animated.View>
|
|
59
|
+
<ThemedView style={styles.content}>{children}</ThemedView>
|
|
60
|
+
</Animated.ScrollView>
|
|
61
|
+
</ThemedView>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const styles = StyleSheet.create({
|
|
66
|
+
container: {
|
|
67
|
+
flex: 1,
|
|
68
|
+
},
|
|
69
|
+
header: {
|
|
70
|
+
height: 250,
|
|
71
|
+
overflow: "hidden",
|
|
72
|
+
},
|
|
73
|
+
content: {
|
|
74
|
+
flex: 1,
|
|
75
|
+
padding: 32,
|
|
76
|
+
gap: 24,
|
|
77
|
+
borderRadius: 16,
|
|
78
|
+
overflow: "hidden",
|
|
79
|
+
marginTop: -12
|
|
80
|
+
}
|
|
81
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Text, StyleSheet } from "react-native";
|
|
2
|
+
|
|
3
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
4
|
+
|
|
5
|
+
export type TextProps = {
|
|
6
|
+
lightColor?: string;
|
|
7
|
+
darkColor?: string;
|
|
8
|
+
} & { type?: "default" | "title" | "defaultSemiBold" | "subtitle" | "link" } & Text["props"];
|
|
9
|
+
|
|
10
|
+
export function ThemedText(props: TextProps) {
|
|
11
|
+
const {
|
|
12
|
+
style,
|
|
13
|
+
lightColor,
|
|
14
|
+
darkColor,
|
|
15
|
+
type = "default",
|
|
16
|
+
...otherProps
|
|
17
|
+
} = props;
|
|
18
|
+
const color = useThemeColor({ light: lightColor, dark: darkColor }, "text");
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Text
|
|
22
|
+
style={[
|
|
23
|
+
{ color },
|
|
24
|
+
type === "default" ? styles.default : undefined,
|
|
25
|
+
type === "title" ? styles.title : undefined,
|
|
26
|
+
type === "defaultSemiBold" ? styles.defaultSemiBold : undefined,
|
|
27
|
+
type === "subtitle" ? styles.subtitle : undefined,
|
|
28
|
+
type === "link" ? styles.link : undefined,
|
|
29
|
+
style,
|
|
30
|
+
]}
|
|
31
|
+
{...otherProps}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const styles = StyleSheet.create({
|
|
37
|
+
default: {
|
|
38
|
+
fontSize: 16,
|
|
39
|
+
lineHeight: 24,
|
|
40
|
+
},
|
|
41
|
+
defaultSemiBold: {
|
|
42
|
+
fontSize: 16,
|
|
43
|
+
lineHeight: 24,
|
|
44
|
+
fontWeight: "600",
|
|
45
|
+
},
|
|
46
|
+
title: {
|
|
47
|
+
fontSize: 32,
|
|
48
|
+
fontWeight: "bold",
|
|
49
|
+
lineHeight: 32,
|
|
50
|
+
},
|
|
51
|
+
subtitle: {
|
|
52
|
+
fontSize: 20,
|
|
53
|
+
fontWeight: "bold",
|
|
54
|
+
},
|
|
55
|
+
link: {
|
|
56
|
+
lineHeight: 30,
|
|
57
|
+
fontSize: 16,
|
|
58
|
+
color: "#2e78b7",
|
|
59
|
+
},
|
|
60
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { View } from "react-native";
|
|
2
|
+
|
|
3
|
+
import { useThemeColor } from "@/hooks/useThemeColor";
|
|
4
|
+
|
|
5
|
+
export type ViewProps = {
|
|
6
|
+
lightColor?: string;
|
|
7
|
+
darkColor?: string;
|
|
8
|
+
} & View["props"];
|
|
9
|
+
|
|
10
|
+
export function ThemedView(props: ViewProps) {
|
|
11
|
+
const { style, lightColor, darkColor, ...otherProps } = props;
|
|
12
|
+
const backgroundColor = useThemeColor(
|
|
13
|
+
{ light: lightColor, dark: darkColor },
|
|
14
|
+
"background",
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
|
|
4
|
+
import { ThemedText } from '../ThemedText';
|
|
5
|
+
|
|
6
|
+
it(`renders correctly`, () => {
|
|
7
|
+
const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON();
|
|
8
|
+
|
|
9
|
+
expect(tree).toMatchSnapshot();
|
|
10
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`renders correctly 1`] = `
|
|
4
|
+
<Text
|
|
5
|
+
style={
|
|
6
|
+
[
|
|
7
|
+
{
|
|
8
|
+
"color": "#11181C",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"fontSize": 16,
|
|
12
|
+
"lineHeight": 24,
|
|
13
|
+
},
|
|
14
|
+
undefined,
|
|
15
|
+
undefined,
|
|
16
|
+
undefined,
|
|
17
|
+
undefined,
|
|
18
|
+
undefined,
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
>
|
|
22
|
+
Snapshot test!
|
|
23
|
+
</Text>
|
|
24
|
+
`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
|
|
2
|
+
|
|
3
|
+
import Ionicons from "@expo/vector-icons/Ionicons";
|
|
4
|
+
import { type ComponentProps } from "react";
|
|
5
|
+
|
|
6
|
+
export function TabBarIcon(props: {
|
|
7
|
+
name: ComponentProps<typeof Ionicons>["name"];
|
|
8
|
+
color: string;
|
|
9
|
+
}) {
|
|
10
|
+
return <Ionicons size={28} style={{ marginBottom: -3 }} {...props} />;
|
|
11
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Below are the colors that are used in the app. The colors are defined in the light and dark mode.
|
|
3
|
+
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const tintColorLight = "#0a7ea4";
|
|
7
|
+
const tintColorDark = "#fff";
|
|
8
|
+
|
|
9
|
+
export const Colors = {
|
|
10
|
+
light: {
|
|
11
|
+
text: "#11181C",
|
|
12
|
+
background: "#fff",
|
|
13
|
+
tint: tintColorLight,
|
|
14
|
+
icon: "#687076",
|
|
15
|
+
tabIconDefault: "#687076",
|
|
16
|
+
tabIconSelected: tintColorLight,
|
|
17
|
+
},
|
|
18
|
+
dark: {
|
|
19
|
+
text: "#ECEDEE",
|
|
20
|
+
background: "#151718",
|
|
21
|
+
tint: tintColorDark,
|
|
22
|
+
icon: "#9BA1A6",
|
|
23
|
+
tabIconDefault: "#9BA1A6",
|
|
24
|
+
tabIconSelected: tintColorDark,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useColorScheme } from "react-native";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// NOTE: The default React Native styling doesn't support server rendering.
|
|
2
|
+
// Server rendered styles should not change between the first render of the HTML
|
|
3
|
+
// and the first render on the client. Typically, web developers will use CSS media queries
|
|
4
|
+
// to render different styles on the client and server, these aren't directly supported in React Native
|
|
5
|
+
// but can be achieved using a styling library like Nativewind.
|
|
6
|
+
export function useColorScheme() {
|
|
7
|
+
return "light";
|
|
8
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learn more about light and dark modes:
|
|
3
|
+
* https://docs.expo.io/guides/color-schemes/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useColorScheme } from "react-native";
|
|
7
|
+
import { Colors } from "@/constants/Colors";
|
|
8
|
+
|
|
9
|
+
export function useThemeColor(
|
|
10
|
+
props: { light?: string; dark?: string },
|
|
11
|
+
colorName: keyof typeof Colors.light & keyof typeof Colors.dark,
|
|
12
|
+
) {
|
|
13
|
+
const theme = useColorScheme() ?? "light";
|
|
14
|
+
const colorFromProps = props[theme];
|
|
15
|
+
|
|
16
|
+
if (colorFromProps) {
|
|
17
|
+
return colorFromProps;
|
|
18
|
+
} else {
|
|
19
|
+
return Colors[theme][colorName];
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "expo-template-default",
|
|
3
|
+
"main": "expo-router/entry",
|
|
4
|
+
"version": "51.0.1",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "expo start",
|
|
7
|
+
"reset-project": "./scripts/reset-project.js",
|
|
8
|
+
"android": "expo start --android",
|
|
9
|
+
"ios": "expo start --ios",
|
|
10
|
+
"web": "expo start --web",
|
|
11
|
+
"test": "jest --watchAll"
|
|
12
|
+
},
|
|
13
|
+
"jest": {
|
|
14
|
+
"preset": "jest-expo"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@expo/vector-icons": "^14.0.0",
|
|
18
|
+
"@react-navigation/drawer": "^6.6.6",
|
|
19
|
+
"@react-navigation/native": "^6.0.2",
|
|
20
|
+
"expo": "~51.0.0-beta.0",
|
|
21
|
+
"expo-font": "~12.0.0",
|
|
22
|
+
"expo-linking": "~6.3.0",
|
|
23
|
+
"expo-router": "~3.5.0",
|
|
24
|
+
"expo-splash-screen": "~0.27.0",
|
|
25
|
+
"expo-status-bar": "~1.12.0",
|
|
26
|
+
"expo-system-ui": "~3.0.0",
|
|
27
|
+
"expo-web-browser": "~13.0.0",
|
|
28
|
+
"react": "18.2.0",
|
|
29
|
+
"react-dom": "18.2.0",
|
|
30
|
+
"react-native": "0.74.0-rc.9",
|
|
31
|
+
"react-native-gesture-handler": "~2.16.0",
|
|
32
|
+
"react-native-reanimated": "~3.9.0-rc.1",
|
|
33
|
+
"react-native-safe-area-context": "4.10.0-rc.1",
|
|
34
|
+
"react-native-screens": "~3.31.0-rc.1",
|
|
35
|
+
"react-native-web": "~0.19.10"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@babel/core": "^7.20.0",
|
|
39
|
+
"@types/jest": "^29.5.12",
|
|
40
|
+
"@types/react": "~18.2.45",
|
|
41
|
+
"@types/react-test-renderer": "^18.0.7",
|
|
42
|
+
"jest": "^29.2.1",
|
|
43
|
+
"jest-expo": "~51.0.0",
|
|
44
|
+
"react-test-renderer": "18.2.0",
|
|
45
|
+
"typescript": "^5.1.3"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This script is used to reset the project to a blank state.
|
|
5
|
+
* It moves the /app directory to /app-example and creates a new /app directory with an index.tsx and _layout.tsx file.
|
|
6
|
+
* You can remove the `reset-project` script from package.json and safely delete this file after running it.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
|
|
12
|
+
const oldDirPath = path.join(__dirname, "app");
|
|
13
|
+
const newDirPath = path.join(__dirname, "app-example");
|
|
14
|
+
const newAppDirPath = path.join(__dirname, "app");
|
|
15
|
+
|
|
16
|
+
const indexContent = `import { Text, View } from "react-native";
|
|
17
|
+
|
|
18
|
+
export default function Index() {
|
|
19
|
+
return (
|
|
20
|
+
<View
|
|
21
|
+
style={{
|
|
22
|
+
flex: 1,
|
|
23
|
+
justifyContent: "center",
|
|
24
|
+
alignItems: "center",
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<Text>Edit app/index.tsx to edit this screen.</Text>
|
|
28
|
+
</View>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const layoutContent = `import { Stack } from "expo-router";
|
|
34
|
+
|
|
35
|
+
export default function RootLayout() {
|
|
36
|
+
return (
|
|
37
|
+
<Stack>
|
|
38
|
+
<Stack.Screen name="index" />
|
|
39
|
+
</Stack>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
fs.rename(oldDirPath, newDirPath, (error) => {
|
|
45
|
+
if (error) {
|
|
46
|
+
return console.error(`Error renaming directory: ${error}`);
|
|
47
|
+
}
|
|
48
|
+
console.log("/app moved to /app-example.");
|
|
49
|
+
|
|
50
|
+
fs.mkdir(newAppDirPath, { recursive: true }, (error) => {
|
|
51
|
+
if (error) {
|
|
52
|
+
return console.error(`Error creating new app directory: ${error}`);
|
|
53
|
+
}
|
|
54
|
+
console.log("New /app directory created.");
|
|
55
|
+
|
|
56
|
+
const indexPath = path.join(newAppDirPath, "index.tsx");
|
|
57
|
+
fs.writeFile(indexPath, indexContent, (error) => {
|
|
58
|
+
if (error) {
|
|
59
|
+
return console.error(`Error creating index.tsx: ${error}`);
|
|
60
|
+
}
|
|
61
|
+
console.log("app/index.tsx created.");
|
|
62
|
+
|
|
63
|
+
const layoutPath = path.join(newAppDirPath, "_layout.tsx");
|
|
64
|
+
fs.writeFile(layoutPath, layoutContent, (error) => {
|
|
65
|
+
if (error) {
|
|
66
|
+
return console.error(`Error creating _layout.tsx: ${error}`);
|
|
67
|
+
}
|
|
68
|
+
console.log("app/_layout.tsx created.");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
package/tsconfig.json
ADDED