fix gluestackUI
2
ArtisanConnect/.gitignore
vendored
@@ -34,5 +34,3 @@ yarn-error.*
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
app-example
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# Welcome to your Expo app 👋
|
||||
|
||||
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
|
||||
|
||||
## Get started
|
||||
|
||||
1. Install dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Start the app
|
||||
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
|
||||
In the output, you'll find options to open the app in a
|
||||
|
||||
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
|
||||
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
|
||||
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
|
||||
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
|
||||
|
||||
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
|
||||
|
||||
## Get a fresh project
|
||||
|
||||
When you're ready, run:
|
||||
|
||||
```bash
|
||||
npm run reset-project
|
||||
```
|
||||
|
||||
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
|
||||
|
||||
## Learn more
|
||||
|
||||
To learn more about developing your project with Expo, look at the following resources:
|
||||
|
||||
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
|
||||
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
|
||||
|
||||
## Join the community
|
||||
|
||||
Join our community of developers creating universal apps.
|
||||
|
||||
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
|
||||
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
|
||||
@@ -2,40 +2,31 @@
|
||||
"expo": {
|
||||
"name": "ArtisanConnect",
|
||||
"slug": "ArtisanConnect",
|
||||
"scheme": "Artisanconnect",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/images/icon.png",
|
||||
"scheme": "myapp",
|
||||
"userInterfaceStyle": "automatic",
|
||||
"icon": "./assets/icon.png",
|
||||
"userInterfaceStyle": "light",
|
||||
"newArchEnabled": true,
|
||||
"splash": {
|
||||
"image": "./assets/splash-icon.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"ios": {
|
||||
"supportsTablet": true
|
||||
},
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"foregroundImage": "./assets/images/adaptive-icon.png",
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#ffffff"
|
||||
}
|
||||
},
|
||||
"web": {
|
||||
"bundler": "metro",
|
||||
"output": "static",
|
||||
"favicon": "./assets/images/favicon.png"
|
||||
"favicon": "./assets/favicon.png"
|
||||
},
|
||||
"plugins": [
|
||||
"expo-router",
|
||||
[
|
||||
"expo-splash-screen",
|
||||
{
|
||||
"image": "./assets/images/splash-icon.png",
|
||||
"imageWidth": 200,
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff"
|
||||
}
|
||||
]
|
||||
],
|
||||
"experiments": {
|
||||
"typedRoutes": true
|
||||
}
|
||||
"expo-router"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Link, Stack } from 'expo-router';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { ThemedText } from '@/components/ThemedText';
|
||||
import { ThemedView } from '@/components/ThemedView';
|
||||
|
||||
export default function NotFoundScreen() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen options={{ title: 'Oops!' }} />
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedText type="title">This screen doesn't exist.</ThemedText>
|
||||
<Link href="/" 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,
|
||||
},
|
||||
});
|
||||
11
ArtisanConnect/app/_layout.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import {Tabs} from 'expo-router';
|
||||
import "@/global.css";
|
||||
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider';
|
||||
|
||||
export default function RootLayout(){
|
||||
return (
|
||||
<GluestackUIProvider>
|
||||
<Tabs/>
|
||||
</GluestackUIProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Stack } from "expo-router";
|
||||
import "@/global.css";
|
||||
import { GluestackUIProvider } from "@/components/ui/gluestack-ui-provider";
|
||||
import { Appearance } from "react-native";
|
||||
import {Colors} from "@/constants/Colors"
|
||||
|
||||
export default function RootLayout() {
|
||||
const colorScheme = Appearance.getColorScheme();
|
||||
|
||||
const theme = colorScheme === 'dark' ? Colors.dark :
|
||||
Colors.light;
|
||||
return (
|
||||
<GluestackUIProvider mode="light"><Stack screenOptions={{ headerStyle: {backgroundColor:theme.background},
|
||||
headerTintColor: theme.text
|
||||
}}>
|
||||
<Stack.Screen name="index"/>
|
||||
<Stack.Screen name="+not-found" options={{headerShown:false}}/>
|
||||
|
||||
</Stack></GluestackUIProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +1,12 @@
|
||||
import { Text, View, Pressable } from "react-native";
|
||||
import { Link } from "expo-router";
|
||||
|
||||
import { Button, ButtonText } from "@/components/ui/button"
|
||||
|
||||
export default function Index() {
|
||||
import { View, Text } from 'react-native';
|
||||
import { Button, ButtonText } from '@/components/ui/button';
|
||||
export default function Home(){
|
||||
return (
|
||||
<View>
|
||||
|
||||
<Link href="/notices" asChild>
|
||||
<Pressable>
|
||||
<Text>Notices</Text>
|
||||
</Pressable>
|
||||
</Link>
|
||||
|
||||
<Button size="md" variant="solid" action="primary">
|
||||
<ButtonText>Zobacz ogłoszenie</ButtonText>
|
||||
</Button>
|
||||
<Text>Home</Text>
|
||||
<Button>
|
||||
<ButtonText>Click Me</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,8 @@
|
||||
import { Appearance, StyleSheet, Platform,
|
||||
SafeAreaView,FlatList, ScrollView, View, Text } from "react-native";
|
||||
import { View, Text } from 'react-native';
|
||||
import NoticeCard from '@/components/NoticeCard';
|
||||
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { MENU_ITEMS } from "@/constants/NoticeItems";
|
||||
|
||||
export default function MenuScreen(){
|
||||
const colorScheme = Appearance.getColorScheme();
|
||||
|
||||
const theme = colorScheme === 'dark' ? Colors.dark : Colors.light;
|
||||
|
||||
const Container = Platform.OS === 'web' ? ScrollView : SafeAreaView;
|
||||
|
||||
return(
|
||||
<Container>
|
||||
<FlatList
|
||||
data={MENU_ITEMS}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
renderItem={({ item }) => (
|
||||
<View>
|
||||
<Text>{item.title}</Text>
|
||||
</View>
|
||||
)}
|
||||
></FlatList>
|
||||
</Container>
|
||||
)
|
||||
export default function Catalog() {
|
||||
return (
|
||||
<NoticeCard/>
|
||||
);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@@ -1,45 +0,0 @@
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
import { StyleSheet, TouchableOpacity } from 'react-native';
|
||||
|
||||
import { ThemedText } from '@/components/ThemedText';
|
||||
import { ThemedView } from '@/components/ThemedView';
|
||||
import { IconSymbol } from '@/components/ui/IconSymbol';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Link } from 'expo-router';
|
||||
import { openBrowserAsync } from 'expo-web-browser';
|
||||
import { type ComponentProps } from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: string };
|
||||
|
||||
export function ExternalLink({ href, ...rest }: Props) {
|
||||
return (
|
||||
<Link
|
||||
target="_blank"
|
||||
{...rest}
|
||||
href={href}
|
||||
onPress={async (event) => {
|
||||
if (Platform.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);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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,40 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import Animated, {
|
||||
useSharedValue,
|
||||
useAnimatedStyle,
|
||||
withTiming,
|
||||
withRepeat,
|
||||
withSequence,
|
||||
} from 'react-native-reanimated';
|
||||
|
||||
import { ThemedText } from '@/components/ThemedText';
|
||||
|
||||
export function HelloWave() {
|
||||
const rotationAnimation = useSharedValue(0);
|
||||
|
||||
useEffect(() => {
|
||||
rotationAnimation.value = withRepeat(
|
||||
withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })),
|
||||
4 // Run the animation 4 times
|
||||
);
|
||||
}, []);
|
||||
|
||||
const animatedStyle = useAnimatedStyle(() => ({
|
||||
transform: [{ rotate: `${rotationAnimation.value}deg` }],
|
||||
}));
|
||||
|
||||
return (
|
||||
<Animated.View style={animatedStyle}>
|
||||
<ThemedText style={styles.text}>👋</ThemedText>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
text: {
|
||||
fontSize: 28,
|
||||
lineHeight: 32,
|
||||
marginTop: -6,
|
||||
},
|
||||
});
|
||||
58
ArtisanConnect/components/NoticeCard.jsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Box } from "@/components/ui/box";
|
||||
import { Button, ButtonText } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Heading } from "@/components/ui/heading";
|
||||
import { Image } from "@/components/ui/image";
|
||||
import { Text } from "@/components/ui/text";
|
||||
import { VStack } from "@/components/ui/vstack";
|
||||
import { Icon } from "@/components/ui/icon";
|
||||
|
||||
export default function NoticeCard() {
|
||||
return (
|
||||
<Card className="p-5 rounded-lg max-w-[360px] m-3">
|
||||
<Image
|
||||
source={{
|
||||
uri: 'https://gluestack.github.io/public-blog-video-assets/saree.png',
|
||||
}}
|
||||
className="mb-6 h-[240px] w-full rounded-md aspect-[4/3]"
|
||||
alt="image"
|
||||
/>
|
||||
<Text
|
||||
className="text-sm font-normal mb-2 text-typography-700"
|
||||
>
|
||||
Fashion Clothing
|
||||
</Text>
|
||||
<VStack className="mb-6">
|
||||
<Heading size="md" className="mb-4">
|
||||
Cotton Kurta
|
||||
</Heading>
|
||||
{/* <Text size="sm">
|
||||
Floral embroidered notch neck thread work cotton kurta in white and
|
||||
black.
|
||||
</Text> */}
|
||||
</VStack>
|
||||
<Box
|
||||
className="flex-col sm:flex-row"
|
||||
>
|
||||
<Button
|
||||
className="px-4 py-2 mr-0 mb-3 sm:mr-3 sm:mb-0 sm:flex-1"
|
||||
>
|
||||
<ButtonText size="sm">Add to cart</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="px-4 py-2 border-outline-300 sm:flex-1"
|
||||
>
|
||||
<ButtonText
|
||||
size="sm"
|
||||
className="text-typography-600"
|
||||
>
|
||||
Wishlist
|
||||
</ButtonText>
|
||||
{/* <Icon as={FavouriteIcon} className="text-typography-500 m-2 w-4 h-4" /> */}
|
||||
</Button>
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import type { PropsWithChildren, ReactElement } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import Animated, {
|
||||
interpolate,
|
||||
useAnimatedRef,
|
||||
useAnimatedStyle,
|
||||
useScrollViewOffset,
|
||||
} from 'react-native-reanimated';
|
||||
|
||||
import { ThemedView } from '@/components/ThemedView';
|
||||
import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
|
||||
const HEADER_HEIGHT = 250;
|
||||
|
||||
type Props = PropsWithChildren<{
|
||||
headerImage: ReactElement;
|
||||
headerBackgroundColor: { dark: string; light: string };
|
||||
}>;
|
||||
|
||||
export default function ParallaxScrollView({
|
||||
children,
|
||||
headerImage,
|
||||
headerBackgroundColor,
|
||||
}: Props) {
|
||||
const colorScheme = useColorScheme() ?? 'light';
|
||||
const scrollRef = useAnimatedRef<Animated.ScrollView>();
|
||||
const scrollOffset = useScrollViewOffset(scrollRef);
|
||||
const bottom = useBottomTabOverflow();
|
||||
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 (
|
||||
<ThemedView style={styles.container}>
|
||||
<Animated.ScrollView
|
||||
ref={scrollRef}
|
||||
scrollEventThrottle={16}
|
||||
scrollIndicatorInsets={{ bottom }}
|
||||
contentContainerStyle={{ paddingBottom: bottom }}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.header,
|
||||
{ backgroundColor: headerBackgroundColor[colorScheme] },
|
||||
headerAnimatedStyle,
|
||||
]}>
|
||||
{headerImage}
|
||||
</Animated.View>
|
||||
<ThemedView style={styles.content}>{children}</ThemedView>
|
||||
</Animated.ScrollView>
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
header: {
|
||||
height: HEADER_HEIGHT,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
padding: 32,
|
||||
gap: 16,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
@@ -1,60 +0,0 @@
|
||||
import { Text, type TextProps, StyleSheet } from 'react-native';
|
||||
|
||||
import { useThemeColor } from '@/hooks/useThemeColor';
|
||||
|
||||
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/useThemeColor';
|
||||
|
||||
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} />;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
import { ThemedText } from '../ThemedText';
|
||||
|
||||
it(`renders correctly`, () => {
|
||||
const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<Text
|
||||
style={
|
||||
[
|
||||
{
|
||||
"color": "#11181C",
|
||||
},
|
||||
{
|
||||
"fontSize": 16,
|
||||
"lineHeight": 24,
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
Snapshot test!
|
||||
</Text>
|
||||
`;
|
||||
@@ -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,43 +0,0 @@
|
||||
// This file is a fallback for using MaterialIcons on Android and web.
|
||||
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
import { SymbolWeight } from 'expo-symbols';
|
||||
import React from 'react';
|
||||
import { OpaqueColorValue, StyleProp, ViewStyle } from 'react-native';
|
||||
|
||||
// Add your SFSymbol to MaterialIcons mappings here.
|
||||
const MAPPING = {
|
||||
// See MaterialIcons here: https://icons.expo.fyi
|
||||
// See SF Symbols in the SF Symbols app on Mac.
|
||||
'house.fill': 'home',
|
||||
'paperplane.fill': 'send',
|
||||
'chevron.left.forwardslash.chevron.right': 'code',
|
||||
'chevron.right': 'chevron-right',
|
||||
} as Partial<
|
||||
Record<
|
||||
import('expo-symbols').SymbolViewProps['name'],
|
||||
React.ComponentProps<typeof MaterialIcons>['name']
|
||||
>
|
||||
>;
|
||||
|
||||
export type IconSymbolName = keyof typeof MAPPING;
|
||||
|
||||
/**
|
||||
* An icon component that uses native SFSymbols on iOS, and MaterialIcons on Android and web. This ensures a consistent look across platforms, and optimal resource usage.
|
||||
*
|
||||
* Icon `name`s are based on SFSymbols and require manual mapping to MaterialIcons.
|
||||
*/
|
||||
export function IconSymbol({
|
||||
name,
|
||||
size = 24,
|
||||
color,
|
||||
style,
|
||||
}: {
|
||||
name: IconSymbolName;
|
||||
size?: number;
|
||||
color: string | OpaqueColorValue;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
weight?: SymbolWeight;
|
||||
}) {
|
||||
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
export default function BlurTabBarBackground() {
|
||||
return (
|
||||
<BlurView
|
||||
// System chrome material automatically adapts to the system's theme
|
||||
// and matches the native tab bar appearance on iOS.
|
||||
tint="systemChromeMaterial"
|
||||
intensity={100}
|
||||
style={StyleSheet.absoluteFill}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function useBottomTabOverflow() {
|
||||
const tabHeight = useBottomTabBarHeight();
|
||||
const { bottom } = useSafeAreaInsets();
|
||||
return tabHeight - bottom;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// This is a shim for web and Android where the tab bar is generally opaque.
|
||||
export default undefined;
|
||||
|
||||
export function useBottomTabOverflow() {
|
||||
return 0;
|
||||
}
|
||||
19
ArtisanConnect/components/ui/box/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { View, ViewProps } from 'react-native';
|
||||
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
import { boxStyle } from './styles';
|
||||
|
||||
type IBoxProps = ViewProps &
|
||||
VariantProps<typeof boxStyle> & { className?: string };
|
||||
|
||||
const Box = React.forwardRef<React.ComponentRef<typeof View>, IBoxProps>(
|
||||
function Box({ className, ...props }, ref) {
|
||||
return (
|
||||
<View ref={ref} {...props} className={boxStyle({ class: className })} />
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Box.displayName = 'Box';
|
||||
export { Box };
|
||||
19
ArtisanConnect/components/ui/box/index.web.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { boxStyle } from './styles';
|
||||
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
|
||||
type IBoxProps = React.ComponentPropsWithoutRef<'div'> &
|
||||
VariantProps<typeof boxStyle> & { className?: string };
|
||||
|
||||
const Box = React.forwardRef<HTMLDivElement, IBoxProps>(function Box(
|
||||
{ className, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div ref={ref} className={boxStyle({ class: className })} {...props} />
|
||||
);
|
||||
});
|
||||
|
||||
Box.displayName = 'Box';
|
||||
export { Box };
|
||||
10
ArtisanConnect/components/ui/box/styles.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { tva } from '@gluestack-ui/nativewind-utils/tva';
|
||||
import { isWeb } from '@gluestack-ui/nativewind-utils/IsWeb';
|
||||
|
||||
const baseStyle = isWeb
|
||||
? 'flex flex-col relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none'
|
||||
: '';
|
||||
|
||||
export const boxStyle = tva({
|
||||
base: baseStyle,
|
||||
});
|
||||
26
ArtisanConnect/components/ui/card/index.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
import { View, ViewProps } from 'react-native';
|
||||
import { cardStyle } from './styles';
|
||||
|
||||
type ICardProps = ViewProps &
|
||||
VariantProps<typeof cardStyle> & { className?: string };
|
||||
|
||||
const Card = React.forwardRef<React.ComponentRef<typeof View>, ICardProps>(
|
||||
function Card(
|
||||
{ className, size = 'md', variant = 'elevated', ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<View
|
||||
className={cardStyle({ size, variant, class: className })}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Card.displayName = 'Card';
|
||||
|
||||
export { Card };
|
||||
23
ArtisanConnect/components/ui/card/index.web.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { cardStyle } from './styles';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
|
||||
type ICardProps = React.ComponentPropsWithoutRef<'div'> &
|
||||
VariantProps<typeof cardStyle>;
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, ICardProps>(function Card(
|
||||
{ className, size = 'md', variant = 'elevated', ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={cardStyle({ size, variant, class: className })}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Card.displayName = 'Card';
|
||||
|
||||
export { Card };
|
||||
20
ArtisanConnect/components/ui/card/styles.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { tva } from '@gluestack-ui/nativewind-utils/tva';
|
||||
import { isWeb } from '@gluestack-ui/nativewind-utils/IsWeb';
|
||||
const baseStyle = isWeb ? 'flex flex-col relative z-0' : '';
|
||||
|
||||
export const cardStyle = tva({
|
||||
base: baseStyle,
|
||||
variants: {
|
||||
size: {
|
||||
sm: 'p-3 rounded',
|
||||
md: 'p-4 rounded-md',
|
||||
lg: 'p-6 rounded-xl',
|
||||
},
|
||||
variant: {
|
||||
elevated: 'bg-background-0',
|
||||
outline: 'border border-outline-200 ',
|
||||
ghost: 'rounded-none',
|
||||
filled: 'bg-background-50',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -4,7 +4,8 @@ import { View, ViewProps } from 'react-native';
|
||||
import { OverlayProvider } from '@gluestack-ui/overlay';
|
||||
import { ToastProvider } from '@gluestack-ui/toast';
|
||||
import { useColorScheme } from 'nativewind';
|
||||
import { ModeType } from './types';
|
||||
|
||||
export type ModeType = 'light' | 'dark' | 'system';
|
||||
|
||||
export function GluestackUIProvider({
|
||||
mode = 'light',
|
||||
|
||||
@@ -5,7 +5,8 @@ import { OverlayProvider } from '@gluestack-ui/overlay';
|
||||
import { ToastProvider } from '@gluestack-ui/toast';
|
||||
import { setFlushStyles } from '@gluestack-ui/nativewind-utils/flush';
|
||||
import { script } from './script';
|
||||
import { ModeType } from './types';
|
||||
|
||||
export type ModeType = 'light' | 'dark' | 'system';
|
||||
|
||||
const variableStyleTagId = 'nativewind-style';
|
||||
const createStyle = (styleTagId: string) => {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export type ModeType = 'light' | 'dark' | 'system';
|
||||
220
ArtisanConnect/components/ui/heading/index.tsx
Normal file
@@ -0,0 +1,220 @@
|
||||
import React, { forwardRef, memo } from 'react';
|
||||
import { H1, H2, H3, H4, H5, H6 } from '@expo/html-elements';
|
||||
import { headingStyle } from './styles';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
import { cssInterop } from 'nativewind';
|
||||
|
||||
type IHeadingProps = VariantProps<typeof headingStyle> &
|
||||
React.ComponentPropsWithoutRef<typeof H1> & {
|
||||
as?: React.ElementType;
|
||||
};
|
||||
|
||||
cssInterop(H1, { className: 'style' });
|
||||
cssInterop(H2, { className: 'style' });
|
||||
cssInterop(H3, { className: 'style' });
|
||||
cssInterop(H4, { className: 'style' });
|
||||
cssInterop(H5, { className: 'style' });
|
||||
cssInterop(H6, { className: 'style' });
|
||||
|
||||
const MappedHeading = memo(
|
||||
forwardRef<React.ComponentRef<typeof H1>, IHeadingProps>(
|
||||
function MappedHeading(
|
||||
{
|
||||
size,
|
||||
className,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) {
|
||||
switch (size) {
|
||||
case '5xl':
|
||||
case '4xl':
|
||||
case '3xl':
|
||||
return (
|
||||
<H1
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
// @ts-expect-error : type issue
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case '2xl':
|
||||
return (
|
||||
<H2
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
// @ts-expect-error : type issue
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case 'xl':
|
||||
return (
|
||||
<H3
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
// @ts-expect-error : type issue
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case 'lg':
|
||||
return (
|
||||
<H4
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
// @ts-expect-error : type issue
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case 'md':
|
||||
return (
|
||||
<H5
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
// @ts-expect-error : type issue
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case 'sm':
|
||||
case 'xs':
|
||||
return (
|
||||
<H6
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
// @ts-expect-error : type issue
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<H4
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
// @ts-expect-error : type issue
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const Heading = memo(
|
||||
forwardRef<React.ComponentRef<typeof H1>, IHeadingProps>(function Heading(
|
||||
{ className, size = 'lg', as: AsComp, ...props },
|
||||
ref
|
||||
) {
|
||||
const {
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
} = props;
|
||||
|
||||
if (AsComp) {
|
||||
return (
|
||||
<AsComp
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MappedHeading className={className} size={size} ref={ref} {...props} />
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
Heading.displayName = 'Heading';
|
||||
|
||||
export { Heading };
|
||||
202
ArtisanConnect/components/ui/heading/index.web.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import React, { forwardRef, memo } from 'react';
|
||||
import { headingStyle } from './styles';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
type IHeadingProps = VariantProps<typeof headingStyle> &
|
||||
React.ComponentPropsWithoutRef<'h1'> & {
|
||||
as?: React.ElementType;
|
||||
};
|
||||
|
||||
const MappedHeading = memo(
|
||||
forwardRef<HTMLHeadingElement, IHeadingProps>(function MappedHeading(
|
||||
{
|
||||
size,
|
||||
className,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) {
|
||||
switch (size) {
|
||||
case '5xl':
|
||||
case '4xl':
|
||||
case '3xl':
|
||||
return (
|
||||
<h1
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case '2xl':
|
||||
return (
|
||||
<h2
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case 'xl':
|
||||
return (
|
||||
<h3
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case 'lg':
|
||||
return (
|
||||
<h4
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case 'md':
|
||||
return (
|
||||
<h5
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
case 'sm':
|
||||
case 'xs':
|
||||
return (
|
||||
<h6
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<h4
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const Heading = memo(
|
||||
forwardRef<HTMLHeadingElement, IHeadingProps>(function Heading(
|
||||
{ className, size = 'lg', as: AsComp, ...props },
|
||||
ref
|
||||
) {
|
||||
const {
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
} = props;
|
||||
|
||||
if (AsComp) {
|
||||
return (
|
||||
<AsComp
|
||||
className={headingStyle({
|
||||
size,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MappedHeading className={className} size={size} ref={ref} {...props} />
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
Heading.displayName = 'Heading';
|
||||
|
||||
export { Heading };
|
||||
43
ArtisanConnect/components/ui/heading/styles.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { tva } from '@gluestack-ui/nativewind-utils/tva';
|
||||
import { isWeb } from '@gluestack-ui/nativewind-utils/IsWeb';
|
||||
const baseStyle = isWeb
|
||||
? 'font-sans tracking-sm bg-transparent border-0 box-border display-inline list-none margin-0 padding-0 position-relative text-start no-underline whitespace-pre-wrap word-wrap-break-word'
|
||||
: '';
|
||||
|
||||
export const headingStyle = tva({
|
||||
base: `text-typography-900 font-bold font-heading tracking-sm my-0 ${baseStyle}`,
|
||||
variants: {
|
||||
isTruncated: {
|
||||
true: 'truncate',
|
||||
},
|
||||
bold: {
|
||||
true: 'font-bold',
|
||||
},
|
||||
underline: {
|
||||
true: 'underline',
|
||||
},
|
||||
strikeThrough: {
|
||||
true: 'line-through',
|
||||
},
|
||||
sub: {
|
||||
true: 'text-xs',
|
||||
},
|
||||
italic: {
|
||||
true: 'italic',
|
||||
},
|
||||
highlight: {
|
||||
true: 'bg-yellow-500',
|
||||
},
|
||||
size: {
|
||||
'5xl': 'text-6xl',
|
||||
'4xl': 'text-5xl',
|
||||
'3xl': 'text-4xl',
|
||||
'2xl': 'text-3xl',
|
||||
'xl': 'text-2xl',
|
||||
'lg': 'text-xl',
|
||||
'md': 'text-lg',
|
||||
'sm': 'text-base',
|
||||
'xs': 'text-sm',
|
||||
},
|
||||
},
|
||||
});
|
||||
1585
ArtisanConnect/components/ui/icon/index.tsx
Normal file
1574
ArtisanConnect/components/ui/icon/index.web.tsx
Normal file
49
ArtisanConnect/components/ui/image/index.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { createImage } from '@gluestack-ui/image';
|
||||
import { Platform, Image as RNImage } from 'react-native';
|
||||
import { tva } from '@gluestack-ui/nativewind-utils/tva';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
|
||||
const imageStyle = tva({
|
||||
base: 'max-w-full',
|
||||
variants: {
|
||||
size: {
|
||||
'2xs': 'h-6 w-6',
|
||||
'xs': 'h-10 w-10',
|
||||
'sm': 'h-16 w-16',
|
||||
'md': 'h-20 w-20',
|
||||
'lg': 'h-24 w-24',
|
||||
'xl': 'h-32 w-32',
|
||||
'2xl': 'h-64 w-64',
|
||||
'full': 'h-full w-full',
|
||||
'none': '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const UIImage = createImage({ Root: RNImage });
|
||||
|
||||
type ImageProps = VariantProps<typeof imageStyle> &
|
||||
React.ComponentProps<typeof UIImage>;
|
||||
const Image = React.forwardRef<
|
||||
React.ComponentRef<typeof UIImage>,
|
||||
ImageProps & { className?: string }
|
||||
>(function Image({ size = 'md', className, ...props }, ref) {
|
||||
return (
|
||||
<UIImage
|
||||
className={imageStyle({ size, class: className })}
|
||||
{...props}
|
||||
ref={ref}
|
||||
// @ts-expect-error : web only
|
||||
style={
|
||||
Platform.OS === 'web'
|
||||
? { height: 'revert-layer', width: 'revert-layer' }
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Image.displayName = 'Image';
|
||||
export { Image };
|
||||
48
ArtisanConnect/components/ui/text/index.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
import { Text as RNText } from 'react-native';
|
||||
import { textStyle } from './styles';
|
||||
|
||||
type ITextProps = React.ComponentProps<typeof RNText> &
|
||||
VariantProps<typeof textStyle>;
|
||||
|
||||
const Text = React.forwardRef<React.ComponentRef<typeof RNText>, ITextProps>(
|
||||
function Text(
|
||||
{
|
||||
className,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
size = 'md',
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<RNText
|
||||
className={textStyle({
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
size,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Text.displayName = 'Text';
|
||||
|
||||
export { Text };
|
||||
45
ArtisanConnect/components/ui/text/index.web.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
import { textStyle } from './styles';
|
||||
|
||||
type ITextProps = React.ComponentProps<'span'> & VariantProps<typeof textStyle>;
|
||||
|
||||
const Text = React.forwardRef<React.ComponentRef<'span'>, ITextProps>(
|
||||
function Text(
|
||||
{
|
||||
className,
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
size = 'md',
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
...props
|
||||
}: { className?: string } & ITextProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<span
|
||||
className={textStyle({
|
||||
isTruncated,
|
||||
bold,
|
||||
underline,
|
||||
strikeThrough,
|
||||
size,
|
||||
sub,
|
||||
italic,
|
||||
highlight,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Text.displayName = 'Text';
|
||||
|
||||
export { Text };
|
||||
47
ArtisanConnect/components/ui/text/styles.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { tva } from '@gluestack-ui/nativewind-utils/tva';
|
||||
import { isWeb } from '@gluestack-ui/nativewind-utils/IsWeb';
|
||||
|
||||
const baseStyle = isWeb
|
||||
? 'font-sans tracking-sm my-0 bg-transparent border-0 box-border display-inline list-none margin-0 padding-0 position-relative text-start no-underline whitespace-pre-wrap word-wrap-break-word'
|
||||
: '';
|
||||
|
||||
export const textStyle = tva({
|
||||
base: `text-typography-700 font-body ${baseStyle}`,
|
||||
|
||||
variants: {
|
||||
isTruncated: {
|
||||
true: 'web:truncate',
|
||||
},
|
||||
bold: {
|
||||
true: 'font-bold',
|
||||
},
|
||||
underline: {
|
||||
true: 'underline',
|
||||
},
|
||||
strikeThrough: {
|
||||
true: 'line-through',
|
||||
},
|
||||
size: {
|
||||
'2xs': 'text-2xs',
|
||||
'xs': 'text-xs',
|
||||
'sm': 'text-sm',
|
||||
'md': 'text-base',
|
||||
'lg': 'text-lg',
|
||||
'xl': 'text-xl',
|
||||
'2xl': 'text-2xl',
|
||||
'3xl': 'text-3xl',
|
||||
'4xl': 'text-4xl',
|
||||
'5xl': 'text-5xl',
|
||||
'6xl': 'text-6xl',
|
||||
},
|
||||
sub: {
|
||||
true: 'text-xs',
|
||||
},
|
||||
italic: {
|
||||
true: 'italic',
|
||||
},
|
||||
highlight: {
|
||||
true: 'bg-yellow-500',
|
||||
},
|
||||
},
|
||||
});
|
||||
24
ArtisanConnect/components/ui/vstack/index.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { vstackStyle } from './styles';
|
||||
|
||||
type IVStackProps = React.ComponentProps<typeof View> &
|
||||
VariantProps<typeof vstackStyle>;
|
||||
|
||||
const VStack = React.forwardRef<React.ComponentRef<typeof View>, IVStackProps>(
|
||||
function VStack({ className, space, reversed, ...props }, ref) {
|
||||
return (
|
||||
<View
|
||||
className={vstackStyle({ space, reversed, class: className })}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
VStack.displayName = 'VStack';
|
||||
|
||||
export { VStack };
|
||||
23
ArtisanConnect/components/ui/vstack/index.web.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
|
||||
import { vstackStyle } from './styles';
|
||||
|
||||
type IVStackProps = React.ComponentProps<'div'> &
|
||||
VariantProps<typeof vstackStyle>;
|
||||
|
||||
const VStack = React.forwardRef<React.ComponentRef<'div'>, IVStackProps>(
|
||||
function VStack({ className, space, reversed, ...props }, ref) {
|
||||
return (
|
||||
<div
|
||||
className={vstackStyle({ space, reversed, class: className })}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
VStack.displayName = 'VStack';
|
||||
|
||||
export { VStack };
|
||||
25
ArtisanConnect/components/ui/vstack/styles.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { isWeb } from '@gluestack-ui/nativewind-utils/IsWeb';
|
||||
import { tva } from '@gluestack-ui/nativewind-utils/tva';
|
||||
|
||||
const baseStyle = isWeb
|
||||
? 'flex flex-col relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none'
|
||||
: '';
|
||||
|
||||
export const vstackStyle = tva({
|
||||
base: `flex-col ${baseStyle}`,
|
||||
variants: {
|
||||
space: {
|
||||
'xs': 'gap-1',
|
||||
'sm': 'gap-2',
|
||||
'md': 'gap-3',
|
||||
'lg': 'gap-4',
|
||||
'xl': 'gap-5',
|
||||
'2xl': 'gap-6',
|
||||
'3xl': 'gap-7',
|
||||
'4xl': 'gap-8',
|
||||
},
|
||||
reversed: {
|
||||
true: 'flex-col-reverse',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,26 +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/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
|
||||
*/
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
export const MENU_ITEMS = [
|
||||
{
|
||||
"id" : 1,
|
||||
"title": "Home",
|
||||
},
|
||||
{
|
||||
"id" : 2,
|
||||
"title" : "Notices"
|
||||
}
|
||||
]
|
||||
@@ -4,7 +4,7 @@
|
||||
"css": "global.css"
|
||||
},
|
||||
"app": {
|
||||
"entry": "app/_layout.tsx",
|
||||
"entry": "app/_layout.jsx",
|
||||
"components": "components/ui"
|
||||
}
|
||||
}
|
||||
@@ -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/Colors';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
3467
ArtisanConnect/package-lock.json
generated
@@ -1,65 +1,43 @@
|
||||
{
|
||||
"name": "artisanconnect",
|
||||
"main": "expo-router/entry",
|
||||
"version": "1.0.0",
|
||||
"main": "expo-router/entry",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"reset-project": "node ./scripts/reset-project.js",
|
||||
"android": "DARK_MODE=media expo start --android",
|
||||
"ios": "DARK_MODE=media expo start --ios",
|
||||
"web": "DARK_MODE=media expo start --web",
|
||||
"test": "jest --watchAll",
|
||||
"lint": "expo lint"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-expo"
|
||||
"android": "DARK_MODE=media DARK_MODE=media expo start --android",
|
||||
"ios": "DARK_MODE=media DARK_MODE=media expo start --ios",
|
||||
"web": "DARK_MODE=media DARK_MODE=media expo start --web"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^14.0.2",
|
||||
"@expo/html-elements": "^0.4.2",
|
||||
"@gluestack-ui/button": "^1.0.14",
|
||||
"@gluestack-ui/icon": "^0.1.27",
|
||||
"@gluestack-ui/image": "^0.1.17",
|
||||
"@gluestack-ui/nativewind-utils": "^1.0.26",
|
||||
"@gluestack-ui/overlay": "^0.1.22",
|
||||
"@gluestack-ui/toast": "^1.0.9",
|
||||
"@react-navigation/bottom-tabs": "^7.2.0",
|
||||
"@react-navigation/native": "^7.0.14",
|
||||
"babel-plugin-module-resolver": "^5.0.2",
|
||||
"expo": "~52.0.42",
|
||||
"expo-blur": "~14.0.3",
|
||||
"expo": "~52.0.46",
|
||||
"expo-constants": "~17.0.8",
|
||||
"expo-font": "~13.0.4",
|
||||
"expo-haptics": "~14.0.1",
|
||||
"expo-linking": "~7.0.5",
|
||||
"expo-router": "~4.0.20",
|
||||
"expo-splash-screen": "~0.29.22",
|
||||
"expo-status-bar": "~2.0.1",
|
||||
"expo-symbols": "~0.2.2",
|
||||
"expo-system-ui": "~4.0.9",
|
||||
"expo-web-browser": "~14.0.2",
|
||||
"nativewind": "^4.1.23",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-native": "0.76.9",
|
||||
"react-native-css-interop": "^0.1.22",
|
||||
"react-native-gesture-handler": "~2.20.2",
|
||||
"react-native-reanimated": "~3.16.1",
|
||||
"react-native-safe-area-context": "4.12.0",
|
||||
"react-native-reanimated": "^3.17.4",
|
||||
"react-native-safe-area-context": "^5.4.0",
|
||||
"react-native-screens": "~4.4.0",
|
||||
"react-native-svg": "15.8.0",
|
||||
"react-native-svg": "^15.2.0",
|
||||
"react-native-web": "~0.19.13",
|
||||
"react-native-webview": "13.12.5",
|
||||
"tailwindcss": "^3.4.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@babel/core": "^7.20.0",
|
||||
"@types/react": "~18.3.12",
|
||||
"@types/react-test-renderer": "^18.3.0",
|
||||
"jest": "^29.2.1",
|
||||
"jest-expo": "~52.0.6",
|
||||
"jscodeshift": "^0.15.2",
|
||||
"react-test-renderer": "18.3.1",
|
||||
"typescript": "^5.3.3"
|
||||
"jscodeshift": "^0.15.2"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* This script is used to reset the project to a blank state.
|
||||
* It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file.
|
||||
* You can remove the `reset-project` script from package.json and safely delete this file after running it.
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const readline = require("readline");
|
||||
|
||||
const root = process.cwd();
|
||||
const oldDirs = ["app", "components", "hooks", "constants", "scripts"];
|
||||
const exampleDir = "app-example";
|
||||
const newAppDir = "app";
|
||||
const exampleDirPath = path.join(root, exampleDir);
|
||||
|
||||
const indexContent = `import { Text, View } from "react-native";
|
||||
|
||||
export default function Index() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Text>Edit app/index.tsx to edit this screen.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
`;
|
||||
|
||||
const layoutContent = `import { Stack } from "expo-router";
|
||||
|
||||
export default function RootLayout() {
|
||||
return <Stack />;
|
||||
}
|
||||
`;
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
const moveDirectories = async (userInput) => {
|
||||
try {
|
||||
if (userInput === "y") {
|
||||
// Create the app-example directory
|
||||
await fs.promises.mkdir(exampleDirPath, { recursive: true });
|
||||
console.log(`📁 /${exampleDir} directory created.`);
|
||||
}
|
||||
|
||||
// Move old directories to new app-example directory or delete them
|
||||
for (const dir of oldDirs) {
|
||||
const oldDirPath = path.join(root, dir);
|
||||
if (fs.existsSync(oldDirPath)) {
|
||||
if (userInput === "y") {
|
||||
const newDirPath = path.join(root, exampleDir, dir);
|
||||
await fs.promises.rename(oldDirPath, newDirPath);
|
||||
console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`);
|
||||
} else {
|
||||
await fs.promises.rm(oldDirPath, { recursive: true, force: true });
|
||||
console.log(`❌ /${dir} deleted.`);
|
||||
}
|
||||
} else {
|
||||
console.log(`➡️ /${dir} does not exist, skipping.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Create new /app directory
|
||||
const newAppDirPath = path.join(root, newAppDir);
|
||||
await fs.promises.mkdir(newAppDirPath, { recursive: true });
|
||||
console.log("\n📁 New /app directory created.");
|
||||
|
||||
// Create index.tsx
|
||||
const indexPath = path.join(newAppDirPath, "index.tsx");
|
||||
await fs.promises.writeFile(indexPath, indexContent);
|
||||
console.log("📄 app/index.tsx created.");
|
||||
|
||||
// Create _layout.tsx
|
||||
const layoutPath = path.join(newAppDirPath, "_layout.tsx");
|
||||
await fs.promises.writeFile(layoutPath, layoutContent);
|
||||
console.log("📄 app/_layout.tsx created.");
|
||||
|
||||
console.log("\n✅ Project reset complete. Next steps:");
|
||||
console.log(
|
||||
`1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${
|
||||
userInput === "y"
|
||||
? `\n3. Delete the /${exampleDir} directory when you're done referencing it.`
|
||||
: ""
|
||||
}`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`❌ Error during script execution: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
rl.question(
|
||||
"Do you want to move existing files to /app-example instead of deleting them? (Y/n): ",
|
||||
(answer) => {
|
||||
const userInput = answer.trim().toLowerCase() || "y";
|
||||
if (userInput === "y" || userInput === "n") {
|
||||
moveDirectories(userInput).finally(() => rl.close());
|
||||
} else {
|
||||
console.log("❌ Invalid input. Please enter 'Y' or 'N'.");
|
||||
rl.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"extends": "expo/tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
@@ -11,12 +9,8 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.jsx",
|
||||
".expo/types/**/*.ts",
|
||||
"expo-env.d.ts",
|
||||
"nativewind-env.d.ts"
|
||||
]
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"extends": "expo/tsconfig.base"
|
||||
}
|
||||