mirror of
https://github.com/kennethnym/aris.git
synced 2026-03-20 09:01:19 +00:00
Compare commits
3 Commits
kn/feat/as
...
bf94c6aff6
| Author | SHA1 | Date | |
|---|---|---|---|
|
bf94c6aff6
|
|||
|
80be2b711b
|
|||
|
fc08f828f2
|
@@ -1,36 +0,0 @@
|
|||||||
import { Tabs } from "expo-router"
|
|
||||||
import React from "react"
|
|
||||||
|
|
||||||
import { HapticTab } from "@/components/haptic-tab"
|
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol"
|
|
||||||
import { Colors } from "@/constants/theme"
|
|
||||||
import { useColorScheme } from "@/hooks/use-color-scheme"
|
|
||||||
|
|
||||||
export default function TabLayout() {
|
|
||||||
const colorScheme = useColorScheme()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tabs
|
|
||||||
screenOptions={{
|
|
||||||
tabBarActiveTintColor: Colors[colorScheme ?? "light"].tint,
|
|
||||||
headerShown: false,
|
|
||||||
tabBarButton: HapticTab,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Tabs.Screen
|
|
||||||
name="index"
|
|
||||||
options={{
|
|
||||||
title: "Home",
|
|
||||||
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Tabs.Screen
|
|
||||||
name="explore"
|
|
||||||
options={{
|
|
||||||
title: "Explore",
|
|
||||||
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tabs>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
import { Image } from "expo-image"
|
|
||||||
import { Platform, StyleSheet } from "react-native"
|
|
||||||
|
|
||||||
import { ExternalLink } from "@/components/external-link"
|
|
||||||
import ParallaxScrollView from "@/components/parallax-scroll-view"
|
|
||||||
import { ThemedText } from "@/components/themed-text"
|
|
||||||
import { ThemedView } from "@/components/themed-view"
|
|
||||||
import { Collapsible } from "@/components/ui/collapsible"
|
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol"
|
|
||||||
import { Fonts } from "@/constants/theme"
|
|
||||||
|
|
||||||
export default function TabTwoScreen() {
|
|
||||||
return (
|
|
||||||
<ParallaxScrollView
|
|
||||||
headerBackgroundColor={{ light: "#D0D0D0", dark: "#353636" }}
|
|
||||||
headerImage={
|
|
||||||
<IconSymbol
|
|
||||||
size={310}
|
|
||||||
color="#808080"
|
|
||||||
name="chevron.left.forwardslash.chevron.right"
|
|
||||||
style={styles.headerImage}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ThemedView style={styles.titleContainer}>
|
|
||||||
<ThemedText
|
|
||||||
type="title"
|
|
||||||
style={{
|
|
||||||
fontFamily: Fonts.rounded,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Explore
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedText>This app includes example code to help you get started.</ThemedText>
|
|
||||||
<Collapsible title="File-based routing">
|
|
||||||
<ThemedText>
|
|
||||||
This app has two screens:{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
|
|
||||||
</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{" "}
|
|
||||||
sets up the tab navigator.
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/router/introduction">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Android, iOS, and web support">
|
|
||||||
<ThemedText>
|
|
||||||
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.
|
|
||||||
</ThemedText>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Images">
|
|
||||||
<ThemedText>
|
|
||||||
For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
|
|
||||||
different screen densities
|
|
||||||
</ThemedText>
|
|
||||||
<Image
|
|
||||||
source={require("@assets/images/react-logo.png")}
|
|
||||||
style={{ width: 100, height: 100, alignSelf: "center" }}
|
|
||||||
/>
|
|
||||||
<ExternalLink href="https://reactnative.dev/docs/images">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Light and dark mode components">
|
|
||||||
<ThemedText>
|
|
||||||
This template has light and dark mode support. The{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect
|
|
||||||
what the user's current color scheme is, and so you can adjust UI colors accordingly.
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Animations">
|
|
||||||
<ThemedText>
|
|
||||||
This template includes an example of an animated component. The{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses
|
|
||||||
the powerful{" "}
|
|
||||||
<ThemedText type="defaultSemiBold" style={{ fontFamily: Fonts.mono }}>
|
|
||||||
react-native-reanimated
|
|
||||||
</ThemedText>{" "}
|
|
||||||
library to create a waving hand animation.
|
|
||||||
</ThemedText>
|
|
||||||
{Platform.select({
|
|
||||||
ios: (
|
|
||||||
<ThemedText>
|
|
||||||
The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{" "}
|
|
||||||
component provides a parallax effect for the header image.
|
|
||||||
</ThemedText>
|
|
||||||
),
|
|
||||||
})}
|
|
||||||
</Collapsible>
|
|
||||||
</ParallaxScrollView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
headerImage: {
|
|
||||||
color: "#808080",
|
|
||||||
bottom: -90,
|
|
||||||
left: -35,
|
|
||||||
position: "absolute",
|
|
||||||
},
|
|
||||||
titleContainer: {
|
|
||||||
flexDirection: "row",
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import { Image } from "expo-image"
|
|
||||||
import { Link } from "expo-router"
|
|
||||||
import { Platform, StyleSheet } from "react-native"
|
|
||||||
|
|
||||||
import { HelloWave } from "@/components/hello-wave"
|
|
||||||
import ParallaxScrollView from "@/components/parallax-scroll-view"
|
|
||||||
import { ThemedText } from "@/components/themed-text"
|
|
||||||
import { ThemedView } from "@/components/themed-view"
|
|
||||||
|
|
||||||
export default function HomeScreen() {
|
|
||||||
return (
|
|
||||||
<ParallaxScrollView
|
|
||||||
headerBackgroundColor={{ light: "#A1CEDC", dark: "#1D3D47" }}
|
|
||||||
headerImage={
|
|
||||||
<Image source={require("@assets/images/partial-react-logo.png")} style={styles.reactLogo} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ThemedView style={styles.titleContainer}>
|
|
||||||
<ThemedText type="title">Welcome!</ThemedText>
|
|
||||||
<HelloWave />
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
|
|
||||||
Press{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">
|
|
||||||
{Platform.select({
|
|
||||||
ios: "cmd + d",
|
|
||||||
android: "cmd + m",
|
|
||||||
web: "F12",
|
|
||||||
})}
|
|
||||||
</ThemedText>{" "}
|
|
||||||
to open developer tools.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<Link href="/modal">
|
|
||||||
<Link.Trigger>
|
|
||||||
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
|
|
||||||
</Link.Trigger>
|
|
||||||
<Link.Preview />
|
|
||||||
<Link.Menu>
|
|
||||||
<Link.MenuAction title="Action" icon="cube" onPress={() => alert("Action pressed")} />
|
|
||||||
<Link.MenuAction
|
|
||||||
title="Share"
|
|
||||||
icon="square.and.arrow.up"
|
|
||||||
onPress={() => alert("Share pressed")}
|
|
||||||
/>
|
|
||||||
<Link.Menu title="More" icon="ellipsis">
|
|
||||||
<Link.MenuAction
|
|
||||||
title="Delete"
|
|
||||||
icon="trash"
|
|
||||||
destructive
|
|
||||||
onPress={() => alert("Delete pressed")}
|
|
||||||
/>
|
|
||||||
</Link.Menu>
|
|
||||||
</Link.Menu>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<ThemedText>
|
|
||||||
{`Tap the Explore tab to learn more about what's included in this starter app.`}
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
{`When you're ready, run `}
|
|
||||||
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">app</ThemedText> to{" "}
|
|
||||||
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
</ParallaxScrollView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
titleContainer: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
stepContainer: {
|
|
||||||
gap: 8,
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
reactLogo: {
|
|
||||||
height: 178,
|
|
||||||
width: 290,
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
position: "absolute",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,23 +1,45 @@
|
|||||||
import { DarkTheme, DefaultTheme, ThemeProvider } from "@react-navigation/native"
|
import "react-native-reanimated"
|
||||||
import { Stack } from "expo-router"
|
import { Stack } from "expo-router"
|
||||||
import { StatusBar } from "expo-status-bar"
|
import { StatusBar } from "expo-status-bar"
|
||||||
import "react-native-reanimated"
|
import { useColorScheme } from "react-native"
|
||||||
import { useColorScheme } from "@/hooks/use-color-scheme"
|
import tw, { useDeviceContext } from "twrnc"
|
||||||
|
|
||||||
export const unstable_settings = {
|
|
||||||
anchor: "(tabs)",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
|
useDeviceContext(tw)
|
||||||
const colorScheme = useColorScheme()
|
const colorScheme = useColorScheme()
|
||||||
|
const headerBg = colorScheme === "dark" ? "#1c1917" : "#f5f5f4"
|
||||||
|
const headerTint = colorScheme === "dark" ? "#e7e5e4" : "#1c1917"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
|
<>
|
||||||
<Stack>
|
<Stack
|
||||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
screenOptions={{
|
||||||
<Stack.Screen name="modal" options={{ presentation: "modal", title: "Modal" }} />
|
headerShown: false,
|
||||||
|
contentStyle: { backgroundColor: headerBg },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen
|
||||||
|
name="components/index"
|
||||||
|
options={{
|
||||||
|
headerShown: true,
|
||||||
|
title: "Components",
|
||||||
|
headerStyle: { backgroundColor: headerBg },
|
||||||
|
headerTintColor: headerTint,
|
||||||
|
headerShadowVisible: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="components/[name]"
|
||||||
|
options={{
|
||||||
|
headerShown: true,
|
||||||
|
title: "",
|
||||||
|
headerStyle: { backgroundColor: headerBg },
|
||||||
|
headerTintColor: headerTint,
|
||||||
|
headerShadowVisible: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<StatusBar style="auto" />
|
<StatusBar style="auto" />
|
||||||
</ThemeProvider>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
48
apps/aelis-client/src/app/components/[name].tsx
Normal file
48
apps/aelis-client/src/app/components/[name].tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { useLocalSearchParams, useNavigation } from "expo-router"
|
||||||
|
import { useEffect } from "react"
|
||||||
|
import { ScrollView, View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { buttonShowcase } from "@/components/ui/button.showcase"
|
||||||
|
import { feedCardShowcase } from "@/components/ui/feed-card.showcase"
|
||||||
|
import { monospaceTextShowcase } from "@/components/ui/monospace-text.showcase"
|
||||||
|
import { sansSerifTextShowcase } from "@/components/ui/sans-serif-text.showcase"
|
||||||
|
import { serifTextShowcase } from "@/components/ui/serif-text.showcase"
|
||||||
|
import { type Showcase } from "@/components/showcase"
|
||||||
|
import { SansSerifText } from "@/components/ui/sans-serif-text"
|
||||||
|
|
||||||
|
const showcases: Record<string, Showcase> = {
|
||||||
|
button: buttonShowcase,
|
||||||
|
"feed-card": feedCardShowcase,
|
||||||
|
"serif-text": serifTextShowcase,
|
||||||
|
"sans-serif-text": sansSerifTextShowcase,
|
||||||
|
"monospace-text": monospaceTextShowcase,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ComponentDetailScreen() {
|
||||||
|
const { name } = useLocalSearchParams<{ name: string }>()
|
||||||
|
const navigation = useNavigation()
|
||||||
|
const showcase = showcases[name]
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showcase) {
|
||||||
|
navigation.setOptions({ title: showcase.title })
|
||||||
|
}
|
||||||
|
}, [navigation, showcase])
|
||||||
|
|
||||||
|
if (!showcase) {
|
||||||
|
return (
|
||||||
|
<View style={tw`bg-stone-100 dark:bg-stone-900 flex-1 items-center justify-center`}>
|
||||||
|
<SansSerifText>Component not found</SansSerifText>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShowcaseComponent = showcase.component
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView style={tw`bg-stone-100 dark:bg-stone-900 flex-1`} contentContainerStyle={tw`px-5 pb-10 pt-4 gap-6`}>
|
||||||
|
<ShowcaseComponent />
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
37
apps/aelis-client/src/app/components/index.tsx
Normal file
37
apps/aelis-client/src/app/components/index.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { Link } from "expo-router"
|
||||||
|
import { FlatList, Pressable, View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { SansSerifText } from "@/components/ui/sans-serif-text"
|
||||||
|
|
||||||
|
const components = [
|
||||||
|
{ name: "button", label: "Button" },
|
||||||
|
{ name: "feed-card", label: "FeedCard" },
|
||||||
|
{ name: "serif-text", label: "SerifText" },
|
||||||
|
{ name: "sans-serif-text", label: "SansSerifText" },
|
||||||
|
{ name: "monospace-text", label: "MonospaceText" },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export default function ComponentsScreen() {
|
||||||
|
return (
|
||||||
|
<View style={tw`flex-1`}>
|
||||||
|
<View style={tw`mx-4 mt-4 rounded-xl border border-stone-200 dark:border-stone-800 overflow-hidden`}>
|
||||||
|
<FlatList
|
||||||
|
data={components}
|
||||||
|
keyExtractor={(item) => item.name}
|
||||||
|
scrollEnabled={false}
|
||||||
|
ItemSeparatorComponent={() => (
|
||||||
|
<View style={tw`border-b border-stone-200 dark:border-stone-800`} />
|
||||||
|
)}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<Link href={`/components/${item.name}`} asChild>
|
||||||
|
<Pressable style={tw`px-4 py-3`}>
|
||||||
|
<SansSerifText style={tw`text-base`}>{item.label}</SansSerifText>
|
||||||
|
</Pressable>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
28
apps/aelis-client/src/app/index.tsx
Normal file
28
apps/aelis-client/src/app/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Link } from "expo-router"
|
||||||
|
import { Pressable } from "react-native"
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { FeedCard } from "@/components/ui/feed-card"
|
||||||
|
import { MonospaceText } from "@/components/ui/monospace-text"
|
||||||
|
import { SansSerifText } from "@/components/ui/sans-serif-text"
|
||||||
|
import { SerifText } from "@/components/ui/serif-text"
|
||||||
|
|
||||||
|
export default function HomeScreen() {
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={tw`bg-stone-100 dark:bg-stone-900 flex-1 px-5 pt-6 gap-4`}>
|
||||||
|
<FeedCard>
|
||||||
|
<SerifText style={tw`text-4xl`}>Hello world asdsadsa</SerifText>
|
||||||
|
<SansSerifText style={tw`text-4xl font-bold`}>Hello world</SansSerifText>
|
||||||
|
<MonospaceText style={tw`text-4xl`}>asdjsakljdl</MonospaceText>
|
||||||
|
<Button style={tw`self-start`} label="Test" />
|
||||||
|
</FeedCard>
|
||||||
|
<Link href="/components" asChild>
|
||||||
|
<Pressable>
|
||||||
|
<SansSerifText style={tw`text-teal-600`}>View component library</SansSerifText>
|
||||||
|
</Pressable>
|
||||||
|
</Link>
|
||||||
|
</SafeAreaView>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Link } from "expo-router"
|
|
||||||
import { StyleSheet } from "react-native"
|
|
||||||
|
|
||||||
import { ThemedText } from "@/components/themed-text"
|
|
||||||
import { ThemedView } from "@/components/themed-view"
|
|
||||||
|
|
||||||
export default function ModalScreen() {
|
|
||||||
return (
|
|
||||||
<ThemedView style={styles.container}>
|
|
||||||
<ThemedText type="title">This is a modal</ThemedText>
|
|
||||||
<Link href="/" dismissTo style={styles.link}>
|
|
||||||
<ThemedText type="link">Go to home screen</ThemedText>
|
|
||||||
</Link>
|
|
||||||
</ThemedView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
padding: 20,
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
marginTop: 15,
|
|
||||||
paddingVertical: 15,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { Href, Link } from "expo-router"
|
|
||||||
import { openBrowserAsync, WebBrowserPresentationStyle } from "expo-web-browser"
|
|
||||||
import { type ComponentProps } from "react"
|
|
||||||
|
|
||||||
type Props = Omit<ComponentProps<typeof Link>, "href"> & { href: Href & string }
|
|
||||||
|
|
||||||
export function ExternalLink({ href, ...rest }: Props) {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
target="_blank"
|
|
||||||
{...rest}
|
|
||||||
href={href}
|
|
||||||
onPress={async (event) => {
|
|
||||||
if (process.env.EXPO_OS !== "web") {
|
|
||||||
// Prevent the default behavior of linking to the default browser on native.
|
|
||||||
event.preventDefault()
|
|
||||||
// Open the link in an in-app browser.
|
|
||||||
await openBrowserAsync(href, {
|
|
||||||
presentationStyle: WebBrowserPresentationStyle.AUTOMATIC,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { BottomTabBarButtonProps } from "@react-navigation/bottom-tabs"
|
|
||||||
import { PlatformPressable } from "@react-navigation/elements"
|
|
||||||
import * as Haptics from "expo-haptics"
|
|
||||||
|
|
||||||
export function HapticTab(props: BottomTabBarButtonProps) {
|
|
||||||
return (
|
|
||||||
<PlatformPressable
|
|
||||||
{...props}
|
|
||||||
onPressIn={(ev) => {
|
|
||||||
if (process.env.EXPO_OS === "ios") {
|
|
||||||
// Add a soft haptic feedback when pressing down on the tabs.
|
|
||||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
|
|
||||||
}
|
|
||||||
props.onPressIn?.(ev)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import Animated from "react-native-reanimated"
|
|
||||||
|
|
||||||
export function HelloWave() {
|
|
||||||
return (
|
|
||||||
<Animated.Text
|
|
||||||
style={{
|
|
||||||
fontSize: 28,
|
|
||||||
lineHeight: 32,
|
|
||||||
marginTop: -6,
|
|
||||||
animationName: {
|
|
||||||
"50%": { transform: [{ rotate: "25deg" }] },
|
|
||||||
},
|
|
||||||
animationIterationCount: 4,
|
|
||||||
animationDuration: "300ms",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
👋
|
|
||||||
</Animated.Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import type { PropsWithChildren, ReactElement } from "react"
|
|
||||||
|
|
||||||
import { StyleSheet } from "react-native"
|
|
||||||
import Animated, {
|
|
||||||
interpolate,
|
|
||||||
useAnimatedRef,
|
|
||||||
useAnimatedStyle,
|
|
||||||
useScrollOffset,
|
|
||||||
} from "react-native-reanimated"
|
|
||||||
|
|
||||||
import { ThemedView } from "@/components/themed-view"
|
|
||||||
import { useColorScheme } from "@/hooks/use-color-scheme"
|
|
||||||
import { useThemeColor } from "@/hooks/use-theme-color"
|
|
||||||
|
|
||||||
const HEADER_HEIGHT = 250
|
|
||||||
|
|
||||||
type Props = PropsWithChildren<{
|
|
||||||
headerImage: ReactElement
|
|
||||||
headerBackgroundColor: { dark: string; light: string }
|
|
||||||
}>
|
|
||||||
|
|
||||||
export default function ParallaxScrollView({
|
|
||||||
children,
|
|
||||||
headerImage,
|
|
||||||
headerBackgroundColor,
|
|
||||||
}: Props) {
|
|
||||||
const backgroundColor = useThemeColor({}, "background")
|
|
||||||
const colorScheme = useColorScheme() ?? "light"
|
|
||||||
const scrollRef = useAnimatedRef<Animated.ScrollView>()
|
|
||||||
const scrollOffset = useScrollOffset(scrollRef)
|
|
||||||
const headerAnimatedStyle = useAnimatedStyle(() => {
|
|
||||||
return {
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateY: interpolate(
|
|
||||||
scrollOffset.value,
|
|
||||||
[-HEADER_HEIGHT, 0, HEADER_HEIGHT],
|
|
||||||
[-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75],
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Animated.ScrollView
|
|
||||||
ref={scrollRef}
|
|
||||||
style={{ backgroundColor, flex: 1 }}
|
|
||||||
scrollEventThrottle={16}
|
|
||||||
>
|
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
styles.header,
|
|
||||||
{ backgroundColor: headerBackgroundColor[colorScheme] },
|
|
||||||
headerAnimatedStyle,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{headerImage}
|
|
||||||
</Animated.View>
|
|
||||||
<ThemedView style={styles.content}>{children}</ThemedView>
|
|
||||||
</Animated.ScrollView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
height: HEADER_HEIGHT,
|
|
||||||
overflow: "hidden",
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
flex: 1,
|
|
||||||
padding: 32,
|
|
||||||
gap: 16,
|
|
||||||
overflow: "hidden",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
18
apps/aelis-client/src/components/showcase.tsx
Normal file
18
apps/aelis-client/src/components/showcase.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { SansSerifText } from "./ui/sans-serif-text"
|
||||||
|
|
||||||
|
export type Showcase = {
|
||||||
|
title: string
|
||||||
|
component: React.ComponentType
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<View style={tw`gap-3`}>
|
||||||
|
<SansSerifText style={tw`text-sm text-stone-500 dark:text-stone-400`}>{title}</SansSerifText>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { StyleSheet, Text, type TextProps } from "react-native"
|
|
||||||
|
|
||||||
import { useThemeColor } from "@/hooks/use-theme-color"
|
|
||||||
|
|
||||||
export type ThemedTextProps = TextProps & {
|
|
||||||
lightColor?: string
|
|
||||||
darkColor?: string
|
|
||||||
type?: "default" | "title" | "defaultSemiBold" | "subtitle" | "link"
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ThemedText({
|
|
||||||
style,
|
|
||||||
lightColor,
|
|
||||||
darkColor,
|
|
||||||
type = "default",
|
|
||||||
...rest
|
|
||||||
}: ThemedTextProps) {
|
|
||||||
const color = useThemeColor({ light: lightColor, dark: darkColor }, "text")
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
{ color },
|
|
||||||
type === "default" ? styles.default : undefined,
|
|
||||||
type === "title" ? styles.title : undefined,
|
|
||||||
type === "defaultSemiBold" ? styles.defaultSemiBold : undefined,
|
|
||||||
type === "subtitle" ? styles.subtitle : undefined,
|
|
||||||
type === "link" ? styles.link : undefined,
|
|
||||||
style,
|
|
||||||
]}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
default: {
|
|
||||||
fontSize: 16,
|
|
||||||
lineHeight: 24,
|
|
||||||
},
|
|
||||||
defaultSemiBold: {
|
|
||||||
fontSize: 16,
|
|
||||||
lineHeight: 24,
|
|
||||||
fontWeight: "600",
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontSize: 32,
|
|
||||||
fontWeight: "bold",
|
|
||||||
lineHeight: 32,
|
|
||||||
},
|
|
||||||
subtitle: {
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
lineHeight: 30,
|
|
||||||
fontSize: 16,
|
|
||||||
color: "#0a7ea4",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { View, type ViewProps } from "react-native"
|
|
||||||
|
|
||||||
import { useThemeColor } from "@/hooks/use-theme-color"
|
|
||||||
|
|
||||||
export type ThemedViewProps = ViewProps & {
|
|
||||||
lightColor?: string
|
|
||||||
darkColor?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
|
|
||||||
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, "background")
|
|
||||||
|
|
||||||
return <View style={[{ backgroundColor }, style]} {...otherProps} />
|
|
||||||
}
|
|
||||||
43
apps/aelis-client/src/components/ui/button.showcase.tsx
Normal file
43
apps/aelis-client/src/components/ui/button.showcase.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import Feather from "@expo/vector-icons/Feather"
|
||||||
|
import { View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { Button } from "./button"
|
||||||
|
import { type Showcase, Section } from "../showcase"
|
||||||
|
|
||||||
|
function ButtonShowcase() {
|
||||||
|
return (
|
||||||
|
<View style={tw`gap-6`}>
|
||||||
|
<Section title="Default">
|
||||||
|
<Button style={tw`self-start`} label="Press me" />
|
||||||
|
</Section>
|
||||||
|
<Section title="Leading icon">
|
||||||
|
<Button
|
||||||
|
style={tw`self-start`}
|
||||||
|
label="Add item"
|
||||||
|
leadingIcon={<Feather name="plus" size={18} color="#e7e5e4" />}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
<Section title="Trailing icon">
|
||||||
|
<Button
|
||||||
|
style={tw`self-start`}
|
||||||
|
label="Next"
|
||||||
|
trailingIcon={<Feather name="arrow-right" size={18} color="#e7e5e4" />}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
<Section title="Both icons">
|
||||||
|
<Button
|
||||||
|
style={tw`self-start`}
|
||||||
|
label="Download"
|
||||||
|
leadingIcon={<Feather name="download" size={18} color="#e7e5e4" />}
|
||||||
|
trailingIcon={<Feather name="chevron-down" size={18} color="#e7e5e4" />}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buttonShowcase: Showcase = {
|
||||||
|
title: "Button",
|
||||||
|
component: ButtonShowcase,
|
||||||
|
}
|
||||||
30
apps/aelis-client/src/components/ui/button.tsx
Normal file
30
apps/aelis-client/src/components/ui/button.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { type PressableProps, Pressable, View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { SansSerifText } from "./sans-serif-text"
|
||||||
|
|
||||||
|
type ButtonProps = Omit<PressableProps, "children"> & {
|
||||||
|
label: string
|
||||||
|
leadingIcon?: React.ReactNode
|
||||||
|
trailingIcon?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Button({ style, label, leadingIcon, trailingIcon, ...props }: ButtonProps) {
|
||||||
|
const hasIcons = leadingIcon != null || trailingIcon != null
|
||||||
|
|
||||||
|
const textElement = <SansSerifText style={tw`text-stone-200 font-medium`}>{label}</SansSerifText>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable style={[tw`rounded-full bg-teal-600 px-4 py-3 w-fit`, style]} {...props}>
|
||||||
|
{hasIcons ? (
|
||||||
|
<View style={tw`flex-row items-center gap-1.5`}>
|
||||||
|
{leadingIcon}
|
||||||
|
{textElement}
|
||||||
|
{trailingIcon}
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
textElement
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { PropsWithChildren, useState } from "react"
|
|
||||||
import { StyleSheet, TouchableOpacity } from "react-native"
|
|
||||||
|
|
||||||
import { ThemedText } from "@/components/themed-text"
|
|
||||||
import { ThemedView } from "@/components/themed-view"
|
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol"
|
|
||||||
import { Colors } from "@/constants/theme"
|
|
||||||
import { useColorScheme } from "@/hooks/use-color-scheme"
|
|
||||||
|
|
||||||
export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
|
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
|
||||||
const theme = useColorScheme() ?? "light"
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemedView>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.heading}
|
|
||||||
onPress={() => setIsOpen((value) => !value)}
|
|
||||||
activeOpacity={0.8}
|
|
||||||
>
|
|
||||||
<IconSymbol
|
|
||||||
name="chevron.right"
|
|
||||||
size={18}
|
|
||||||
weight="medium"
|
|
||||||
color={theme === "light" ? Colors.light.icon : Colors.dark.icon}
|
|
||||||
style={{ transform: [{ rotate: isOpen ? "90deg" : "0deg" }] }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ThemedText type="defaultSemiBold">{title}</ThemedText>
|
|
||||||
</TouchableOpacity>
|
|
||||||
{isOpen && <ThemedView style={styles.content}>{children}</ThemedView>}
|
|
||||||
</ThemedView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
heading: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 6,
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
marginTop: 6,
|
|
||||||
marginLeft: 24,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
32
apps/aelis-client/src/components/ui/feed-card.showcase.tsx
Normal file
32
apps/aelis-client/src/components/ui/feed-card.showcase.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { Button } from "./button"
|
||||||
|
import { FeedCard } from "./feed-card"
|
||||||
|
import { SansSerifText } from "./sans-serif-text"
|
||||||
|
import { SerifText } from "./serif-text"
|
||||||
|
import { type Showcase, Section } from "../showcase"
|
||||||
|
|
||||||
|
function FeedCardShowcase() {
|
||||||
|
return (
|
||||||
|
<View style={tw`gap-6`}>
|
||||||
|
<Section title="Default">
|
||||||
|
<FeedCard style={tw`p-4`}>
|
||||||
|
<SansSerifText>Card content goes here</SansSerifText>
|
||||||
|
</FeedCard>
|
||||||
|
</Section>
|
||||||
|
<Section title="With mixed content">
|
||||||
|
<FeedCard style={tw`p-4 gap-2`}>
|
||||||
|
<SerifText style={tw`text-xl`}>Title</SerifText>
|
||||||
|
<SansSerifText>Body text inside a feed card.</SansSerifText>
|
||||||
|
<Button style={tw`self-start mt-2`} label="Action" />
|
||||||
|
</FeedCard>
|
||||||
|
</Section>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const feedCardShowcase: Showcase = {
|
||||||
|
title: "FeedCard",
|
||||||
|
component: FeedCardShowcase,
|
||||||
|
}
|
||||||
6
apps/aelis-client/src/components/ui/feed-card.tsx
Normal file
6
apps/aelis-client/src/components/ui/feed-card.tsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { View, type ViewProps } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
export function FeedCard({ style, ...props }: ViewProps) {
|
||||||
|
return <View style={[tw`border border-stone-200 dark:border-stone-800 rounded-lg`, style]} {...props} />
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import { SymbolView, SymbolViewProps, SymbolWeight } from "expo-symbols"
|
|
||||||
import { StyleProp, ViewStyle } from "react-native"
|
|
||||||
|
|
||||||
export function IconSymbol({
|
|
||||||
name,
|
|
||||||
size = 24,
|
|
||||||
color,
|
|
||||||
style,
|
|
||||||
weight = "regular",
|
|
||||||
}: {
|
|
||||||
name: SymbolViewProps["name"]
|
|
||||||
size?: number
|
|
||||||
color: string
|
|
||||||
style?: StyleProp<ViewStyle>
|
|
||||||
weight?: SymbolWeight
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<SymbolView
|
|
||||||
weight={weight}
|
|
||||||
tintColor={color}
|
|
||||||
resizeMode="scaleAspectFit"
|
|
||||||
name={name}
|
|
||||||
style={[
|
|
||||||
{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
},
|
|
||||||
style,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// Fallback for using MaterialIcons on Android and web.
|
|
||||||
|
|
||||||
import MaterialIcons from "@expo/vector-icons/MaterialIcons"
|
|
||||||
import { SymbolWeight, SymbolViewProps } from "expo-symbols"
|
|
||||||
import { ComponentProps } from "react"
|
|
||||||
import { OpaqueColorValue, type StyleProp, type TextStyle } from "react-native"
|
|
||||||
|
|
||||||
type IconMapping = Record<SymbolViewProps["name"], ComponentProps<typeof MaterialIcons>["name"]>
|
|
||||||
type IconSymbolName = keyof typeof MAPPING
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add your SF Symbols to Material Icons mappings here.
|
|
||||||
* - see Material Icons in the [Icons Directory](https://icons.expo.fyi).
|
|
||||||
* - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app.
|
|
||||||
*/
|
|
||||||
const MAPPING = {
|
|
||||||
"house.fill": "home",
|
|
||||||
"paperplane.fill": "send",
|
|
||||||
"chevron.left.forwardslash.chevron.right": "code",
|
|
||||||
"chevron.right": "chevron-right",
|
|
||||||
} as IconMapping
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web.
|
|
||||||
* This ensures a consistent look across platforms, and optimal resource usage.
|
|
||||||
* Icon `name`s are based on SF Symbols and require manual mapping to Material Icons.
|
|
||||||
*/
|
|
||||||
export function IconSymbol({
|
|
||||||
name,
|
|
||||||
size = 24,
|
|
||||||
color,
|
|
||||||
style,
|
|
||||||
}: {
|
|
||||||
name: IconSymbolName
|
|
||||||
size?: number
|
|
||||||
color: string | OpaqueColorValue
|
|
||||||
style?: StyleProp<TextStyle>
|
|
||||||
weight?: SymbolWeight
|
|
||||||
}) {
|
|
||||||
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { MonospaceText } from "./monospace-text"
|
||||||
|
import { type Showcase, Section } from "../showcase"
|
||||||
|
|
||||||
|
function MonospaceTextShowcase() {
|
||||||
|
return (
|
||||||
|
<View style={tw`gap-6`}>
|
||||||
|
<Section title="Sizes">
|
||||||
|
<View style={tw`gap-2`}>
|
||||||
|
<MonospaceText style={tw`text-sm`}>Small monospace text</MonospaceText>
|
||||||
|
<MonospaceText style={tw`text-base`}>Base monospace text</MonospaceText>
|
||||||
|
<MonospaceText style={tw`text-xl`}>Extra large monospace text</MonospaceText>
|
||||||
|
<MonospaceText style={tw`text-3xl`}>3XL monospace text</MonospaceText>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
<Section title="Code-like usage">
|
||||||
|
<View style={tw`bg-stone-200 dark:bg-stone-800 rounded-lg p-3`}>
|
||||||
|
<MonospaceText style={tw`text-sm`}>{"const x = 42;"}</MonospaceText>
|
||||||
|
<MonospaceText style={tw`text-sm`}>{"console.log(x);"}</MonospaceText>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const monospaceTextShowcase: Showcase = {
|
||||||
|
title: "MonospaceText",
|
||||||
|
component: MonospaceTextShowcase,
|
||||||
|
}
|
||||||
10
apps/aelis-client/src/components/ui/monospace-text.tsx
Normal file
10
apps/aelis-client/src/components/ui/monospace-text.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Text, type TextProps } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
export function MonospaceText({ children, style, ...props }: TextProps) {
|
||||||
|
return (
|
||||||
|
<Text style={[tw`text-stone-800 dark:text-stone-200`, { fontFamily: "Menlo" }, style]} {...props}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { SansSerifText } from "./sans-serif-text"
|
||||||
|
import { type Showcase, Section } from "../showcase"
|
||||||
|
|
||||||
|
function SansSerifTextShowcase() {
|
||||||
|
return (
|
||||||
|
<View style={tw`gap-6`}>
|
||||||
|
<Section title="Sizes">
|
||||||
|
<View style={tw`gap-2`}>
|
||||||
|
<SansSerifText style={tw`text-sm`}>Small sans-serif text</SansSerifText>
|
||||||
|
<SansSerifText style={tw`text-base`}>Base sans-serif text</SansSerifText>
|
||||||
|
<SansSerifText style={tw`text-xl`}>Extra large sans-serif text</SansSerifText>
|
||||||
|
<SansSerifText style={tw`text-3xl`}>3XL sans-serif text</SansSerifText>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
<Section title="Weights">
|
||||||
|
<View style={tw`gap-2`}>
|
||||||
|
<SansSerifText style={tw`font-light`}>Light weight</SansSerifText>
|
||||||
|
<SansSerifText style={tw`font-normal`}>Normal weight</SansSerifText>
|
||||||
|
<SansSerifText style={tw`font-medium`}>Medium weight</SansSerifText>
|
||||||
|
<SansSerifText style={tw`font-semibold`}>Semibold weight</SansSerifText>
|
||||||
|
<SansSerifText style={tw`font-bold`}>Bold weight</SansSerifText>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sansSerifTextShowcase: Showcase = {
|
||||||
|
title: "SansSerifText",
|
||||||
|
component: SansSerifTextShowcase,
|
||||||
|
}
|
||||||
10
apps/aelis-client/src/components/ui/sans-serif-text.tsx
Normal file
10
apps/aelis-client/src/components/ui/sans-serif-text.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Text, type TextProps } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
export function SansSerifText({ children, style, ...props }: TextProps) {
|
||||||
|
return (
|
||||||
|
<Text style={[tw`text-stone-800 dark:text-stone-200`, { fontFamily: "Inter" }, style]} {...props}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
25
apps/aelis-client/src/components/ui/serif-text.showcase.tsx
Normal file
25
apps/aelis-client/src/components/ui/serif-text.showcase.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { View } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
import { SerifText } from "./serif-text"
|
||||||
|
import { type Showcase, Section } from "../showcase"
|
||||||
|
|
||||||
|
function SerifTextShowcase() {
|
||||||
|
return (
|
||||||
|
<View style={tw`gap-6`}>
|
||||||
|
<Section title="Sizes">
|
||||||
|
<View style={tw`gap-2`}>
|
||||||
|
<SerifText style={tw`text-sm`}>Small serif text</SerifText>
|
||||||
|
<SerifText style={tw`text-base`}>Base serif text</SerifText>
|
||||||
|
<SerifText style={tw`text-xl`}>Extra large serif text</SerifText>
|
||||||
|
<SerifText style={tw`text-3xl`}>3XL serif text</SerifText>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const serifTextShowcase: Showcase = {
|
||||||
|
title: "SerifText",
|
||||||
|
component: SerifTextShowcase,
|
||||||
|
}
|
||||||
10
apps/aelis-client/src/components/ui/serif-text.tsx
Normal file
10
apps/aelis-client/src/components/ui/serif-text.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Text, type TextProps } from "react-native"
|
||||||
|
import tw from "twrnc"
|
||||||
|
|
||||||
|
export function SerifText({ children, style, ...props }: TextProps) {
|
||||||
|
return (
|
||||||
|
<Text style={[tw`text-stone-800 dark:text-stone-200`, { fontFamily: "Source Serif 4" }, style]} {...props}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/**
|
|
||||||
* Below are the colors that are used in the app. The colors are defined in the light and dark mode.
|
|
||||||
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Platform } from "react-native"
|
|
||||||
|
|
||||||
const tintColorLight = "#0a7ea4"
|
|
||||||
const tintColorDark = "#fff"
|
|
||||||
|
|
||||||
export const Colors = {
|
|
||||||
light: {
|
|
||||||
text: "#11181C",
|
|
||||||
background: "#fff",
|
|
||||||
tint: tintColorLight,
|
|
||||||
icon: "#687076",
|
|
||||||
tabIconDefault: "#687076",
|
|
||||||
tabIconSelected: tintColorLight,
|
|
||||||
},
|
|
||||||
dark: {
|
|
||||||
text: "#ECEDEE",
|
|
||||||
background: "#151718",
|
|
||||||
tint: tintColorDark,
|
|
||||||
icon: "#9BA1A6",
|
|
||||||
tabIconDefault: "#9BA1A6",
|
|
||||||
tabIconSelected: tintColorDark,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Fonts = Platform.select({
|
|
||||||
ios: {
|
|
||||||
/** iOS `UIFontDescriptorSystemDesignDefault` */
|
|
||||||
sans: "system-ui",
|
|
||||||
/** iOS `UIFontDescriptorSystemDesignSerif` */
|
|
||||||
serif: "ui-serif",
|
|
||||||
/** iOS `UIFontDescriptorSystemDesignRounded` */
|
|
||||||
rounded: "ui-rounded",
|
|
||||||
/** iOS `UIFontDescriptorSystemDesignMonospaced` */
|
|
||||||
mono: "ui-monospace",
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
sans: "normal",
|
|
||||||
serif: "serif",
|
|
||||||
rounded: "normal",
|
|
||||||
mono: "monospace",
|
|
||||||
},
|
|
||||||
web: {
|
|
||||||
sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
|
|
||||||
serif: "Georgia, 'Times New Roman', serif",
|
|
||||||
rounded: "'SF Pro Rounded', 'Hiragino Maru Gothic ProN', Meiryo, 'MS PGothic', sans-serif",
|
|
||||||
mono: "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { useColorScheme } from "react-native"
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { useEffect, useState } from "react"
|
|
||||||
import { useColorScheme as useRNColorScheme } from "react-native"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To support static rendering, this value needs to be re-calculated on the client side for web
|
|
||||||
*/
|
|
||||||
export function useColorScheme() {
|
|
||||||
const [hasHydrated, setHasHydrated] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setHasHydrated(true)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const colorScheme = useRNColorScheme()
|
|
||||||
|
|
||||||
if (hasHydrated) {
|
|
||||||
return colorScheme
|
|
||||||
}
|
|
||||||
|
|
||||||
return "light"
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Learn more about light and dark modes:
|
|
||||||
* https://docs.expo.dev/guides/color-schemes/
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Colors } from "@/constants/theme"
|
|
||||||
import { useColorScheme } from "@/hooks/use-color-scheme"
|
|
||||||
|
|
||||||
export function useThemeColor(
|
|
||||||
props: { light?: string; dark?: string },
|
|
||||||
colorName: keyof typeof Colors.light & keyof typeof Colors.dark,
|
|
||||||
) {
|
|
||||||
const theme = useColorScheme() ?? "light"
|
|
||||||
const colorFromProps = props[theme]
|
|
||||||
|
|
||||||
if (colorFromProps) {
|
|
||||||
return colorFromProps
|
|
||||||
} else {
|
|
||||||
return Colors[theme][colorName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user