Fix auth pages and logic (stub)

pull/6/head
Anthony RICHARD 5 months ago
parent c31fb48123
commit 77b250a52f

@ -2,11 +2,14 @@ import { Stack } from "expo-router";
export default function AuthLayout() {
return (
<Stack initialRouteName="login">
<Stack.Screen name="login" options={{ headerShown: false }} />
<Stack.Screen name="code-send" options={{ headerShown: false }} />
<Stack initialRouteName="log-in">
<Stack.Screen name="log-in" options={{ headerShown: false }} />
<Stack.Screen name="sign-in" options={{ headerShown: false }} />
<Stack.Screen name="reset-password" options={{ headerShown: false }} />
<Stack.Screen
name="reset-password-with-email"
options={{ headerShown: false }}
/>
</Stack>
);
}

@ -1,58 +0,0 @@
// import BackButton from "@/components/BackButton";
// import React from "react";
// import { Card } from "@/components/ui/card";
// import Screen from "@/components/Screen";
// import { Heading } from "@/components/ui/heading";
// import { AntDesign } from "@expo/vector-icons";
// import { VStack } from "@/components/ui/vstack";
// import { Text } from "@/components/ui/text";
// import { Button, ButtonIcon, ButtonText } from "@/components/ui/button";
// import { LockIcon } from "@/components/ui/icon";
// function truncateEmail(email: string) {
// const splitedEmail = email.split("@");
// let hiddenPart = splitedEmail[0][0];
// for (let i = 1; i < splitedEmail[0].length - 1; i++) {
// hiddenPart += "⋆";
// }
// return hiddenPart + splitedEmail[0].slice(-1) + "@" + splitedEmail[1];
// }
// type props = { email?: string };
// export default function CodeSentPage({ email }: props) {
// return (
// <Screen>
// <BackButton icon="close" link={"/login"} action="primary" />
// <Card className="rounded-3xl mt-16">
// <VStack space="lg">
// <AntDesign
// className="bg-green-100 p-4 rounded-3xl h-16 w-16 self-center"
// name="check"
// size={30}
// color={"green"}
// />
// <Heading className="text-center" size="3xl">
// Code envoyé !
// </Heading>
// <VStack space="md">
// <Text className="text-center" size="xl">
// Nous t'avons envoyé le code de vérification à
// </Text>
// <Text className="text-center" size="2xl" bold={true}>
// {truncateEmail(email ?? "test@Optifit.com")}
// </Text>
// <Text className="text-center" size="xl">
// Clique sur renvoyer si tu n'as pas reçu dici quelques secondes !
// 🔥
// </Text>
// </VStack>
// <Button size="xl" action="secondary">
// <ButtonIcon as={LockIcon} />
// <ButtonText>Renvoyer le code</ButtonText>
// </Button>
// </VStack>
// </Card>
// </Screen>
// );
// }

@ -0,0 +1,66 @@
import LoginForm from "@/components/form/LoginForm";
import Button from "@/components/ui/Button";
import Screen from "@/components/ui/Screen";
import Text from "@/components/ui/Text";
import { Feather } from "@expo/vector-icons";
import { Link } from "expo-router";
import { View } from "react-native";
export default function LoginPage() {
interface ISocialNetworkButtons {
icon: any;
}
const socialNetworkButtons: ISocialNetworkButtons[] = [
{ icon: "instagram" },
{ icon: "facebook" },
{ icon: "linkedin" },
];
return (
<Screen>
<View className="justify-center gap-4 h-full">
<View className="gap-2">
<Text position="center" size="3xl" weight="bold">
Connexion à Optifit
</Text>
<Text position="center" size="xl">
Personnalise ton expérience du sport avec Optifit, ton nouveau coach
IA.
</Text>
</View>
<LoginForm />
<View className="flex-row justify-center gap-4">
{socialNetworkButtons.map((socialNetworkButton) => (
<Button
className="w-[4.5rem] h-[4.5rem]"
key={socialNetworkButton.icon}
size="xl"
style="outline"
>
<Feather
name={socialNetworkButton.icon}
size={26}
color={"black"}
/>
</Button>
))}
</View>
<View className="gap-2">
<Text position="center">
Tu n'as pas encore de compte ?{" "}
<Link href="/sign-in">
<Text weight="bold" isLink={true}>
Inscris-toi !
</Text>
</Link>
</Text>
<Link href="/reset-password">
<Text position="center" weight="bold" isLink={true}>
Mot de passe oublié ?
</Text>
</Link>
</View>
</View>
</Screen>
);
}

@ -1,89 +0,0 @@
import Screen from "@/components/ui/Screen";
import CustomText from "@/components/ui/Text";
import { Link } from "expo-router";
import { View } from "react-native";
const socialNetworkButtons: ISocialNetworkButtons[] = [
{ icon: "instagram" },
{ icon: "facebook" },
{ icon: "linkedin" },
];
interface ISocialNetworkButtons {
icon: any;
}
export default function LoginPage() {
return (
<Screen>
<View className="flex flex-col gap-4">
<View className="flex flex-col gap-2">
<CustomText center={true} size="3xl" bold={true}>
Connexion à Optifit
</CustomText>
<CustomText center={true} size="lg">
Personnalise ton expérience du sport avec Optifit, ton nouveau coach
IA.
</CustomText>
</View>
{/* <LoginForm /> */}
<View className="flex flex-col gap-2">
<CustomText center={true}>
Tu n'as pas encore de compte ?
<Link href="/(auth)/sign-in">
<CustomText bold={true} isLink={true}>
{" "}
Inscris-toi !
</CustomText>
</Link>
</CustomText>
<Link href="/reset-password">
<CustomText center={true} bold={true} isLink={true}>
Mot de passe oublié ?
</CustomText>
</Link>
</View>
</View>
</Screen>
// <Screen>
// <Box className="h-full justify-center">
// <VStack space="2xl">
// <VStack space="sm">
// <Heading className="text-center" size="3xl">
// Connexion à Optifit
// </Heading>
// <Text size="lg" className="text-center">
// Personnalise ton expérience du sport avec Optifit, ton nouveau
// coach IA.
// </Text>
// </VStack>
// <LoginForm />
// <ButtonGroup className="justify-center" flexDirection="row">
// {socialNetworkButtons.map((socialNetworkButton) => (
// <Button
// key={socialNetworkButton.icon}
// size="xl"
// variant="outline"
// action="primary"
// >
// <Feather name={socialNetworkButton.icon} size={27} />
// </Button>
// ))}
// </ButtonGroup>
// <VStack>
// <HStack className="justify-center items-center" space="xs">
// <Text>Tu n'as pas encore de compte ?</Text>
// <Link href="/(auth)/sign-in">
// <LinkText bold={true}>Inscris-toi !</LinkText>
// </Link>
// </HStack>
// <Link className="text-center" href="/reset-password">
// <LinkText bold={true}>Mot de passe oublié ?</LinkText>
// </Link>
// </VStack>
// </VStack>
// </Box>
// </Screen>
);
}

@ -0,0 +1,68 @@
import Button from "@/components/ui/Button";
import Screen from "@/components/ui/Screen";
import Text from "@/components/ui/Text";
import TextInput from "@/components/form/FormInput";
import BackButton from "@/components/BackButton";
import { View } from "react-native";
import React from "react";
import CodeSent from "@/components/modals/CodeSent";
import FormError from "@/components/form/FormError";
import { EMPTY_FIELD, INVALID_EMAIL } from "@/components/Errors";
import { isEmail } from "validator";
export default function ResetPasswordWithEmail() {
const [isModalVisible, setIsModalVisible] = React.useState(false);
const [email, setEmail] = React.useState("");
const [error, setError] = React.useState("");
const [isFormValid, setIsFormValid] = React.useState(true);
const validateForm = () => {
setError("");
setIsFormValid(true);
};
const invalidateForm = (error: string) => {
setError(error);
setIsFormValid(false);
};
const onSubmit = () => {
if (email != "") {
if (isEmail(email)) {
validateForm();
setIsModalVisible(true);
} else {
invalidateForm(INVALID_EMAIL);
}
} else {
invalidateForm(EMPTY_FIELD);
}
};
return (
<Screen>
<CodeSent
email={email}
onPress={() => setIsModalVisible(false)}
visible={isModalVisible}
/>
<BackButton icon="close" />
<View className="gap-4">
<Text size="3xl" weight="bold">
Recevoir un code par email
</Text>
<TextInput
beforeIcon="mail"
placeholder="Test@Optifit.com"
onChangeText={setEmail}
value={email}
label={"Adresse mail"}
/>
<FormError isVisible={!isFormValid}>{error}</FormError>
<Button onPress={onSubmit} afterIcon="arrowright">
Recevoir le code
</Button>
</View>
</Screen>
);
}

@ -1,67 +1,49 @@
// import {Text} from "@/components/ui/text";
// import {
// Button,
// ButtonGroup,
// ButtonIcon,
// ButtonText,
// } from "@/components/ui/button";
// import {VStack} from "@/components/ui/vstack";
// import React from "react";
// import {
// ArrowRightIcon,
// LockIcon,
// MailIcon,
// MessageCircleIcon,
// } from "@/components/ui/icon";
// import BackButton from "@/components/BackButton";
// import Screen from "@/components/Screen";
// import {Heading} from "@/components/ui/heading";
// import {Link} from "expo-router";
// import {SafeAreaView} from "react-native";
import BackButton from "@/components/BackButton";
import { AntDesignIconNames } from "@/components/Icons";
import Button from "@/components/ui/Button";
import Screen from "@/components/ui/Screen";
import Text from "@/components/ui/Text";
import { Link } from "expo-router";
import { View } from "react-native";
// const resetButtons: IResetButton[] = [
// {icon: MailIcon, text: "Envoyer par email"},
// {icon: LockIcon, text: "Envoyer par 2FA"},
// {icon: MessageCircleIcon, text: "Envoyer par SMS"},
// ];
export default function ResetPasswordPage() {
interface IResetButton {
icon: AntDesignIconNames;
text: string;
}
// interface IResetButton {
// icon: React.ElementType;
// text: string;
// }
const resetButtons: IResetButton[] = [
{ icon: "mail", text: "Envoyer par email" },
{ icon: "lock", text: "Envoyer par 2FA" },
{ icon: "message1", text: "Envoyer par SMS" },
];
// export default function ResetPasswordPage() {
// return (
// <SafeAreaView>
// <Screen>
// <BackButton link={"/login"}/>
// <VStack space="2xl">
// <VStack space="sm">
// <Heading className="text-center" size="3xl">
// Réinitialisation de ton mot de passe
// </Heading>
// <Text size="lg" className="text-center">
// Selectionne une méthode pour recevoir ton code temporaire
// </Text>
// </VStack>
// <ButtonGroup space="xl">
// {resetButtons.map((resetButton) => (
// <Link key={resetButton.text} href={"/sign-in"} asChild>
// <Button
// className="justify-between"
// size="2xl"
// action="primary"
// variant="outline"
// >
// <ButtonIcon as={resetButton.icon}/>
// <ButtonText>{resetButton.text}</ButtonText>
// <ButtonIcon as={ArrowRightIcon}/>
// </Button>
// </Link>
// ))}
// </ButtonGroup>
// </VStack>
// </Screen>
// </SafeAreaView>
// );
// }
return (
<Screen>
<BackButton />
<View className="gap-4">
<View className="gap-2">
<Text className="text-center" size="3xl" weight="bold">
Réinitialisation de ton mot de passe
</Text>
<Text size="lg" className="text-center">
Selectionne une méthode pour recevoir ton code temporaire !
</Text>
</View>
<View className="gap-4">
{resetButtons.map((resetButton) => (
<Button
key={resetButton.text}
size="2xl"
style="secondary"
beforeIcon={resetButton.icon}
afterIcon="arrowright"
>
<Link href="/reset-password-with-email">{resetButton.text}</Link>
</Button>
))}
</View>
</View>
</Screen>
);
}

@ -1,36 +1,31 @@
// import { Text } from "@/components/ui/text";
// import { VStack } from "@/components/ui/vstack";
// import { Box } from "@/components/ui/box";
// import { HStack } from "@/components/ui/hstack";
// import React from "react";
// import SigninForm from "@/components/form/SigninForm";
// import Screen from "@/components/Screen";
// import { Heading } from "@/components/ui/heading";
// import { Link } from "expo-router";
// import { LinkText } from "@/components/ui/link";
import SigninForm from "@/components/form/SigninForm";
import Screen from "@/components/ui/Screen";
import Text from "@/components/ui/Text";
import { Link } from "expo-router";
import { View } from "react-native";
// export default function SigninPage() {
// return (
// <Screen>
// <Box className="h-full justify-center">
// <VStack space="2xl">
// <VStack space="sm">
// <Heading className="text-center" size="2xl">
// Inscris-toi gratuitement
// </Heading>
// <Text size="lg" className="text-center">
// Génère un programme personnalisé en quelques clics et 1 minute !
// </Text>
// </VStack>
// <SigninForm />
// <HStack className="justify-center items-center" space="xs">
// <Text>Tu as déjà un compte ?</Text>
// <Link href="/sign-in">
// <LinkText bold={true}>Connectes-toi !</LinkText>
// </Link>
// </HStack>
// </VStack>
// </Box>
// </Screen>
// );
// }
export default function SigninPage() {
return (
<Screen>
<View className="justify-center h-full gap-4">
<View className="gap-2">
<Text position="center" size="3xl" weight="bold">
Inscris-toi gratuitement
</Text>
<Text size="xl" position="center">
Génère un programme personnalisé en quelques clics et 1 minute !
</Text>
</View>
<SigninForm />
<Text position="center">
Tu as déjà un compte ?{" "}
<Link href="/log-in">
<Text isLink={true} weight="bold">
Connectes-toi !
</Text>
</Link>
</Text>
</View>
</Screen>
);
}

@ -1,13 +1,24 @@
import {SafeAreaView, Text, View} from "react-native";
import { SafeAreaView, Text, View } from "react-native";
import React from "react";
import Button from "@/components/ui/Button";
import { useSession } from "@/ctx";
import { router } from "expo-router";
export default function ProfileScreen() {
return (
<SafeAreaView>
<View>
<Text className="m-7 font-extrabold">Welcome Screen </Text>
<Text>We will do it soon</Text>
</View>
</SafeAreaView>
);
}
const { signOut } = useSession();
const disconnect = () => {
signOut();
router.replace("/log-in");
};
return (
<SafeAreaView>
<View>
<Text className="m-7 font-extrabold">Welcome Screen </Text>
<Text>We will do it soon</Text>
</View>
<Button onPress={disconnect}>Se déconnecter</Button>
</SafeAreaView>
);
}

@ -1,26 +1,25 @@
import { Redirect, Tabs } from "expo-router";
import { useSession } from "@/ctx";
import { Text } from "react-native";
import React from "react";
import {AntDesign, Ionicons, MaterialIcons} from "@expo/vector-icons";
import { AntDesign, Ionicons, MaterialIcons } from "@expo/vector-icons";
import Loading from "../loading";
export default function TabBarLayout() {
const { session, isLoading } = useSession();
const sizeIcon = 24;
// You can keep the splash screen open, or render a loading screen like we do here.
if (isLoading) {
return <Text>Loading...</Text>;
return <Loading />;
}
// Only require authentication within the (app) group's layout as users
// need to be able to access the (auth) group and sign in again.
/*if (!session) {
if (!session) {
// On web, static rendering will stop here as the user is not authenticated
// in the headless Node process that the pages are rendered in.
return <Redirect href="/login" />;
}*/
return <Redirect href="/log-in" />;
}
return (
<Tabs
screenOptions={{
@ -30,49 +29,60 @@ export default function TabBarLayout() {
<Tabs.Screen
name="(home)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => <Ionicons name="home" size={sizeIcon} color="black" />,
tabBarShowLabel: false,
tabBarIcon: () => (
<Ionicons name="home" size={sizeIcon} color="black" />
),
}}
/>
<Tabs.Screen
name="(exercice)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => <MaterialIcons name="fitness-center" size={sizeIcon} color="black" />,
}}
<Tabs.Screen
name="(exercice)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => (
<MaterialIcons
name="fitness-center"
size={sizeIcon}
color="black"
/>
),
}}
/>
<Tabs.Screen
name="(add)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => <Ionicons name="add-circle-outline" size={sizeIcon} color="black" />,
}}
/>
<Tabs.Screen
name="(profil)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => <MaterialIcons name="account-circle" size={sizeIcon} color="black" />,
}}
/>
<Tabs.Screen
name="(help)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => <AntDesign name="question" size={sizeIcon} color="black" />,
}}
/>
<Tabs.Screen
name="(add)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => (
<Ionicons name="add-circle-outline" size={sizeIcon} color="black" />
),
}}
/>
<Tabs.Screen
name="(profil)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => (
<MaterialIcons
name="account-circle"
size={sizeIcon}
color="black"
/>
),
}}
/>
<Tabs.Screen
name="(help)"
options={{
tabBarShowLabel: false,
tabBarIcon: () => (
<AntDesign name="question" size={sizeIcon} color="black" />
),
}}
/>
</Tabs>
);
}

@ -0,0 +1,12 @@
import Text from "@/components/ui/Text";
import { View } from "react-native";
export default function Loading() {
return (
<View className="justify-center items-center h-full">
<Text size="3xl" weight="bold">
Chargement en cours
</Text>
</View>
);
}

@ -1,23 +1,29 @@
import { router } from "expo-router";
import Button from "./ui/Button";
import { TouchableOpacityProps } from "react-native";
import { AntDesign } from "@expo/vector-icons";
import { Button, ButtonActions } from "./ui/Button";
import { Href, Link } from "expo-router";
import React from "react";
import { AntDesignIconNames } from "./Icons";
type props = {
icon?: any;
link: Href;
action?: ButtonActions;
};
export default function BackButton({ icon, link, action }: props) {
return (
<Button className="h-16 w-16 mb-4" action={action ?? "secondary"}>
<Link href={link}>
<AntDesign
name={icon ?? "arrowleft"}
size={30}
color={action == "primary" ? "white" : "black"}
/>
</Link>
</Button>
);
interface Props extends TouchableOpacityProps {
icon?: AntDesignIconNames;
}
export default React.forwardRef<any, Props>(
(props, ref): React.ReactElement => {
const { icon, onPress } = props;
const defaultOnPress = () => {
router.back();
};
return (
<Button
className="h-16 w-16 mb-4"
onPress={onPress ?? defaultOnPress}
{...ref}
>
<AntDesign name={icon ?? "arrowleft"} size={24} />
</Button>
);
}
);

@ -0,0 +1,58 @@
export type Size = "xs" | "md" | "lg" | "xl" | "2xl" | "3xl";
export type Color = "black" | "white" | "orange" | "red";
export type Weight = "thin" | "normal" | "bold" | "extrabold";
export type Position = "left" | "center" | "right";
export function toTextSize(size: Size): string {
switch (size) {
case "xs":
return "text-xs";
case "md":
return "text-md";
case "lg":
return "text-lg";
case "xl":
return "text-xl";
case "2xl":
return "text-2xl";
case "3xl":
return "text-3xl";
}
}
export function toTextColor(color: Color): string {
switch (color) {
case "black":
return "text-black";
case "white":
return "text-white";
case "orange":
return "text-orange-500";
case "red":
return "text-red-500";
}
}
export function toTextWeight(weight: Weight): string {
switch (weight) {
case "thin":
return "font-thin";
case "normal":
return "font-normal";
case "bold":
return "font-bold";
case "extrabold":
return "font-extrabold";
}
}
export function toTextPosition(position: Position): string {
switch (position) {
case "left":
return "text-left";
case "center":
return "text-center";
case "right":
return "text-right";
}
}

@ -0,0 +1,3 @@
export const EMPTY_FIELD = "Un des champs est vide !";
export const INVALID_EMAIL = "Adresse mail invalide !";
export const NOT_MATCHING_PASSWORD = "Les mots de passe sont différents";

@ -0,0 +1,3 @@
import { AntDesign } from "@expo/vector-icons";
export type AntDesignIconNames = keyof typeof AntDesign.glyphMap;

@ -1,19 +1,29 @@
import {
FormControlError,
FormControlErrorIcon,
FormControlErrorText,
} from "../ui/form-control";
import { AlertCircleIcon } from "../ui/icon";
import React from "react";
import { View } from "react-native";
import Text, { ExtendedTextProps } from "../ui/Text";
import { MaterialIcons } from "@expo/vector-icons";
type props = { text: string };
export default function FormError({ text }: props) {
return (
<FormControlError className="mb-2 border-[1px] border-red-500 p-4 rounded-3xl bg-red-100 gap-2">
<FormControlErrorIcon as={AlertCircleIcon} />
<FormControlErrorText className="text-black font-bold">
{text}
</FormControlErrorText>
</FormControlError>
);
interface Props extends ExtendedTextProps {
isVisible: boolean;
}
export default React.forwardRef<any, Props>(
(props, ref): React.ReactElement => {
const { isVisible, ...rest } = props;
const buildClassName = (): string => {
return (
"flex-row items-center gap-2 bg-red-300 p-4 border-2 border-red-500 rounded-3xl" +
" " +
(isVisible ? "block" : "hidden")
);
};
return (
<View className={buildClassName()} {...ref}>
<MaterialIcons name="error" size={24} color={"red"} />
<Text position="center" weight="bold" {...rest} />
</View>
);
}
);

@ -0,0 +1,49 @@
import React from "react";
import { TextInput, TextInputProps, View } from "react-native";
import { AntDesignIconNames } from "../Icons";
import { AntDesign } from "@expo/vector-icons";
import Text from "../ui/Text";
export interface FormInputProps extends TextInputProps {
afterIcon?: AntDesignIconNames;
label: string;
}
interface Props extends FormInputProps {
beforeIcon: AntDesignIconNames;
}
export default React.forwardRef<any, Props>(
(props, ref): React.ReactElement => {
const {
onBlur,
onChangeText,
value,
beforeIcon,
afterIcon,
label,
placeholder,
onPress,
...rest
} = props;
return (
<View className="gap-2">
<Text weight="bold">{label}</Text>
<View className="flex-row items-center gap-2 px-4 py-2 bg-gray-200 rounded-3xl">
<AntDesign name={beforeIcon} size={24} />
<TextInput
className="grow"
placeholder={placeholder}
onBlur={onBlur}
onChangeText={onChangeText}
value={value}
{...ref}
{...rest}
/>
{afterIcon != null ? (
<AntDesign name={afterIcon} size={24} onPress={onPress} />
) : null}
</View>
</View>
);
}
);

@ -1,75 +1,72 @@
import React from "react";
import { Text } from "react-native";
import { View, ViewProps } from "react-native";
import TextInput from "./FormInput";
import Button from "../ui/Button";
export default function LoginForm() {
const REQUIRED_ERROR = "Au moins un des champs requis est vide !";
const [emailValue, setEmailValue] = React.useState("");
const [isEmailInvalid, setIsEmailInvalid] = React.useState(false);
const [passwordValue, setPasswordValue] = React.useState("");
const [isPasswordInvalid, setIsPasswordInvalid] = React.useState(false);
const [isFormInvalid, setIsFormInvalid] = React.useState(false);
const handleSubmit = () => {
//check for empty fields
setIsEmailInvalid(emailValue == "");
setIsPasswordInvalid(passwordValue == "");
setIsFormInvalid(isEmailInvalid || isPasswordInvalid);
};
import PasswordTextInput from "./SecretTextInput";
import React from "react";
import { EMPTY_FIELD, INVALID_EMAIL } from "../Errors";
import FormError from "./FormError";
import { isEmail } from "validator";
import { useSession } from "@/ctx";
import { router } from "expo-router";
export default React.forwardRef<any, ViewProps>(
(props, ref): React.ReactElement => {
const { ...rest } = props;
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const [error, setError] = React.useState("");
const [isFormValid, setIsFormValid] = React.useState(true);
const { signIn } = useSession();
const validateForm = () => {
setError("");
setIsFormValid(true);
};
return (
<FormControl className="gap-4" isInvalid={isFormInvalid}>
<FormControl isInvalid={isEmailInvalid}>
<FormControlLabel>
<FormControlLabelText bold={true}>Adresse mail</FormControlLabelText>
</FormControlLabel>
<Input variant="outline" size="xl">
<InputIcon color="black" as={MailIcon} />
<InputField
value={emailValue}
onChangeText={setEmailValue}
placeholder="Test@optifit.com"
const invalidateForm = (error: string) => {
setError(error);
setIsFormValid(false);
};
const onSubmit = () => {
if (email != "") {
if (isEmail(email)) {
if (password != "") {
validateForm();
signIn();
router.replace("/HomeScreen");
} else {
invalidateForm(EMPTY_FIELD);
}
} else {
invalidateForm(INVALID_EMAIL);
}
} else {
invalidateForm(EMPTY_FIELD);
}
};
return (
<View className="gap-4" {...ref} {...rest}>
<View className="gap-4">
<TextInput
beforeIcon="mail"
placeholder="Test@Optifit.com"
onChangeText={setEmail}
value={email}
label={"Adresse mail"}
/>
</Input>
</FormControl>
<FormControl isInvalid={isPasswordInvalid}>
<FormControlLabel>
<FormControlLabelText bold={true}>Mot de passe</FormControlLabelText>
</FormControlLabel>
<PasswordField value={passwordValue} onChangeText={setPasswordValue} />
</FormControl>
<VStack>
<FormError text={REQUIRED_ERROR} />
<Button size="xl" onPress={handleSubmit}>
<ButtonText>Se connecter</ButtonText>
<ButtonIcon as={ArrowRightIcon} />
</Button>
</VStack>
</FormControl>
<FormControl className="gap-4">
<FormControl>
<FormControlLabel>
<FormControlLabelText bold={true}>Adresse mail</FormControlLabelText>
</FormControlLabel>
<Input variant="outline" size="xl">
<InputIcon color="black" as={MailIcon} />
<InputField placeholder="Test@optifit.com" />
</Input>
</FormControl>
<FormControl>
<FormControlLabel>
<FormControlLabelText bold={true}>Mot de passe</FormControlLabelText>
</FormControlLabel>
<PasswordField />
</FormControl>
<VStack>
<FormError text={"REQUIRED_ERROR"} />
<Button size="xl">
<ButtonText>Se connecter</ButtonText>
<ButtonIcon as={ArrowRightIcon} />
<PasswordTextInput
label="Mot de passe"
onChangeText={setPassword}
value={password}
/>
</View>
<FormError isVisible={!isFormValid}>{error}</FormError>
<Button onPress={onSubmit} afterIcon="arrowright">
Se connecter
</Button>
</VStack>
</FormControl>
<Link href={"/sign-in"} asChild>
<Button title="Se connecter"/>
</Link>
);
}
</View>
);
}
);

@ -1,31 +0,0 @@
import React from "react";
import { LockIcon, EyeIcon, EyeOffIcon } from "../ui/icon";
import { Input, InputIcon, InputField, InputSlot } from "../ui/input";
import { VariantProps } from "@gluestack-ui/nativewind-utils";
type props = React.ComponentProps<typeof Input> &
VariantProps<typeof InputField>;
export default function PasswordField({ value, onChangeText }: props) {
const [showPassword, setShowPassword] = React.useState(false);
const handleState = () => {
setShowPassword((showState) => {
return !showState;
});
};
return (
<Input variant="outline" size="xl">
<InputIcon color="black" as={LockIcon} />
<InputField
value={value}
onChangeText={onChangeText}
type={showPassword ? "text" : "password"}
placeholder="⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆"
/>
<InputSlot className="pr-3" onPress={handleState}>
<InputIcon as={showPassword ? EyeIcon : EyeOffIcon} />
</InputSlot>
</Input>
);
}

@ -0,0 +1,29 @@
import React, { useState } from "react";
import FormInput, { FormInputProps } from "./FormInput";
export default React.forwardRef<any, FormInputProps>(
(props, ref): React.ReactElement => {
const { onBlur, onChangeText, value, label, ...rest } = props;
const [showPassword, setShowPassword] = useState(false);
const toggleShowPassword = () => {
setShowPassword(!showPassword);
};
return (
<FormInput
label={label}
beforeIcon="lock"
afterIcon={showPassword ? "eye" : "eyeo"}
placeholder="⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆"
onBlur={onBlur}
onChangeText={onChangeText}
value={value}
onPress={toggleShowPassword}
secureTextEntry={!showPassword}
{...ref}
{...rest}
/>
);
}
);

@ -1,125 +1,82 @@
import { Button, ButtonText, ButtonIcon } from "../ui/Button";
import {
FormControl,
FormControlLabel,
FormControlLabelText,
} from "../ui/form-control";
import { MailIcon, ArrowRightIcon } from "../ui/icon";
import { Input, InputIcon, InputField } from "../ui/input";
import { VStack } from "../ui/vstack";
import { View, ViewProps } from "react-native";
import TextInput from "./FormInput";
import Button from "../ui/Button";
import React from "react";
import PasswordTextInput from "./SecretTextInput";
import { isEmail } from "validator";
import { EMPTY_FIELD, INVALID_EMAIL, NOT_MATCHING_PASSWORD } from "../Errors";
import FormError from "./FormError";
import PasswordField from "./PasswordField";
export default function SigninForm() {
const REQUIRED = "Au moins un des champs requis est vide !";
const INVALID_EMAIL = "Adresse mail invalide !";
// TODO Définir ce qu'est un mdp valide
const INVALID_PASSWORD = "Mot de passe invalide !";
const NOT_MATCHING_PASSWORD = "Les mots de passe ne correspondent pas !";
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
export default React.forwardRef<any, ViewProps>(
(props, ref): React.ReactElement => {
const { ...rest } = props;
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const [confirmPassword, setConfirmPassword] = React.useState("");
const [error, setError] = React.useState("");
const [isFormValid, setIsFormValid] = React.useState(true);
const [emailValue, setEmailValue] = React.useState("");
const [isEmailInvalid, setIsEmailInvalid] = React.useState(false);
const [passwordValue, setPasswordValue] = React.useState("");
const [isPasswordInvalid, setIsPasswordInvalid] = React.useState(false);
const [passwordConfirmValue, setPasswordConfirmValue] = React.useState("");
const [isPasswordConfirmInvalid, setIsPasswordConfirmInvalid] =
React.useState(false);
const [isFormInvalid, setIsFormInvalid] = React.useState(false);
const [formError, setFormError] = React.useState("");
const validateForm = () => {
setError("");
setIsFormValid(true);
};
function validateForm() {
setFormError("");
setIsFormInvalid(false);
}
function invalidateForm(error: string) {
setFormError(error);
setIsFormInvalid(true);
}
const handleSubmit = () => {
//check for valid email block
if (emailValue != "") {
if (emailRegex.test(emailValue)) {
setIsEmailInvalid(false);
} else {
setIsEmailInvalid(true);
invalidateForm(INVALID_EMAIL);
return;
}
} else {
setIsEmailInvalid(true);
invalidateForm(REQUIRED);
return;
}
//TODO : check for valid password block
if (passwordValue != "") {
setIsPasswordInvalid(false);
} else {
setIsPasswordInvalid(true);
invalidateForm(REQUIRED);
return;
}
const invalidateForm = (error: string) => {
setError(error);
setIsFormValid(false);
};
//check for valid password confirmation
if (passwordConfirmValue != "") {
if (passwordConfirmValue == passwordValue) {
setIsPasswordConfirmInvalid(false);
validateForm();
const onSubmit = () => {
if (email != "") {
if (isEmail(email)) {
if (password != "") {
if (confirmPassword != "") {
if (confirmPassword == password) {
validateForm();
console.log("tmp");
} else {
invalidateForm(NOT_MATCHING_PASSWORD);
}
} else {
invalidateForm(EMPTY_FIELD);
}
} else {
invalidateForm(EMPTY_FIELD);
}
} else {
invalidateForm(INVALID_EMAIL);
}
} else {
setIsPasswordConfirmInvalid(true);
invalidateForm(NOT_MATCHING_PASSWORD);
return;
invalidateForm(EMPTY_FIELD);
}
} else {
setIsPasswordConfirmInvalid(true);
invalidateForm(REQUIRED);
return;
}
};
};
return (
<FormControl className="gap-4" isInvalid={isFormInvalid}>
<FormControl isInvalid={isEmailInvalid}>
<FormControlLabel>
<FormControlLabelText bold={true}>Adresse mail</FormControlLabelText>
</FormControlLabel>
<Input variant="outline" size="xl">
<InputIcon color="black" as={MailIcon} />
<InputField
value={emailValue}
onChangeText={setEmailValue}
placeholder="Test@optifit.com"
return (
<View className="gap-4" {...ref} {...rest}>
<View className="gap-4">
<TextInput
beforeIcon="mail"
placeholder="Test@Optifit.com"
onChangeText={setEmail}
value={email}
label={"Adresse mail"}
/>
<PasswordTextInput
onChangeText={setPassword}
value={password}
label={"Mot de passe"}
/>
</Input>
</FormControl>
<FormControl isInvalid={isPasswordInvalid}>
<FormControlLabel>
<FormControlLabelText bold={true}>Mot de passe</FormControlLabelText>
</FormControlLabel>
<PasswordField value={passwordValue} onChangeText={setPasswordValue} />
</FormControl>
<FormControl isInvalid={isPasswordConfirmInvalid}>
<FormControlLabel>
<FormControlLabelText bold={true}>
Confirmation du mot de passe
</FormControlLabelText>
</FormControlLabel>
<PasswordField
value={passwordConfirmValue}
onChangeText={setPasswordConfirmValue}
/>
</FormControl>
<VStack>
<FormError text={formError} />
<Button size="xl" onPress={handleSubmit}>
<ButtonText>S'inscrire</ButtonText>
<ButtonIcon as={ArrowRightIcon} />
<PasswordTextInput
onChangeText={setConfirmPassword}
value={confirmPassword}
label={"Confirmation du mot de passe"}
/>
</View>
<FormError isVisible={!isFormValid}>{error}</FormError>
<Button onPress={onSubmit} afterIcon="arrowright">
S'inscrire
</Button>
</VStack>
</FormControl>
);
}
</View>
);
}
);

@ -0,0 +1,50 @@
import BackButton from "@/components/BackButton";
import Button from "@/components/ui/Button";
import { AntDesign } from "@expo/vector-icons";
import { GestureResponderEvent, Modal, ModalProps, View } from "react-native";
import Text from "@/components/ui/Text";
import React from "react";
import Screen from "../ui/Screen";
interface Props extends ModalProps {
email: string;
onPress: (event: GestureResponderEvent) => void;
}
export default React.forwardRef<any, Props>(
(props, ref): React.ReactElement => {
const { email, onPress, ...rest } = props;
return (
<Modal {...ref} animationType="fade" {...rest}>
<Screen>
<BackButton onPress={onPress} />
<View className="gap-4">
<AntDesign
className="bg-green-100 p-4 rounded-3xl h-16 w-16 self-center"
name="check"
size={30}
color={"green"}
/>
<Text position="center" size="3xl">
Code envoyé !
</Text>
<View className="gap-2">
<Text position="center" size="xl">
Nous t'avons envoyé le code de vérification à
</Text>
<Text position="center" size="2xl" weight="bold">
{email ?? "test@Optifit.com"}
</Text>
<Text position="center" size="xl">
Clique sur renvoyer si tu n'as pas reçu dici quelques secondes
! 🔥
</Text>
</View>
<Button style="secondary">Renvoyer le code</Button>
</View>
</Screen>
</Modal>
);
}
);

@ -1,17 +1,106 @@
import React from "react";
import { TouchableOpacity, View } from "react-native";
import { TouchableOpacity, TouchableOpacityProps, View } from "react-native";
import CustomText from "./Text";
import { AntDesign } from "@expo/vector-icons";
import { AntDesignIconNames } from "../Icons";
import { Size } from "../Constants";
type props = { title: string; size: string };
export type ButtonStyle = "default" | "outline" | "secondary";
const Button = ({ title, size }: props) => (
<View className="bg-black rounded-3xl">
<TouchableOpacity className="p-4">
<CustomText center={true} color="white" size={size}>
{title}
</CustomText>
</TouchableOpacity>
</View>
);
//@ts-ignore
interface Props extends TouchableOpacityProps {
size?: Size;
style?: ButtonStyle;
insideClassName?: string;
beforeIcon?: AntDesignIconNames;
afterIcon?: AntDesignIconNames;
}
export default React.forwardRef<any, Props>(
(props, ref): React.ReactElement => {
const {
size,
style,
beforeIcon,
afterIcon,
onPress,
className,
insideClassName,
children,
...rest
} = props;
const getButtonStyle = (): string => {
switch (style ?? "default") {
case "default":
return (
"justify-center items-center bg-black text-white rounded-3xl" +
" " +
className
);
case "outline":
return (
"justify-center items-center border-black text-black border-2 rounded-3xl" +
" " +
className
);
case "secondary":
return (
"justify-center items-center bg-gray-200 text-black rounded-3xl" +
" " +
className
);
}
};
export default Button;
const getIconSize = (): number => {
switch (size ?? "lg") {
case "xs":
return 24;
case "md":
return 24;
case "lg":
return 24;
case "xl":
return 24;
case "2xl":
return 30;
case "3xl":
return 36;
}
};
return (
<View className={getButtonStyle()} {...ref} {...rest}>
<TouchableOpacity
className={"flex-row justify-center items-center gap-2 p-4 w-full"}
onPress={onPress}
>
{beforeIcon != null ? (
<AntDesign
name={beforeIcon}
color={style == null || style == "default" ? "white" : "black"}
size={getIconSize()}
/>
) : null}
<CustomText
position="center"
size={size ?? "lg"}
weight="bold"
color={style == null || style == "default" ? "white" : "black"}
>
{children}
</CustomText>
{afterIcon != null ? (
<AntDesign
className="w-auto"
name={afterIcon}
size={getIconSize()}
color={style == null || style == "default" ? "white" : "black"}
/>
) : null}
</TouchableOpacity>
</View>
);
}
);

@ -1,13 +1,14 @@
import { PropsWithChildren } from "react";
import { View } from "react-native";
import { View, ViewProps } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import React from "react";
type props = PropsWithChildren;
export default function Screen({ children }: props) {
return (
<SafeAreaView>
<View className={"h-full pl-2 pr-2"}>{children}</View>
</SafeAreaView>
);
}
export default React.forwardRef<any, ViewProps>(
(props, ref): React.ReactElement => {
const { children, ...rest } = props;
return (
<SafeAreaView className={"h-full p-4"} {...ref} {...rest}>
<View>{children}</View>
</SafeAreaView>
);
}
);

@ -1,27 +1,62 @@
import { PropsWithChildren } from "react";
import { Text } from "react-native";
import React from "react";
import { Text, TextProps } from "react-native";
import {
Color,
Position,
Size,
toTextColor,
toTextPosition,
toTextSize,
toTextWeight,
Weight,
} from "../Constants";
type props = PropsWithChildren & {
center?: boolean;
color?: string;
size?: string;
bold?: boolean;
export interface ExtendedTextProps extends TextProps {
position?: Position;
color?: Color;
size?: Size;
weight?: Weight;
isLink?: boolean;
};
}
const CustomText = ({ children, center, color, size, bold, isLink }: props) => {
const className =
"text-" +
(size ?? "md") +
" " +
(center ? "text-center" : "") +
" " +
("text-" + (color ?? "black ")) +
" " +
(bold ? "font-bold" : "") +
" " +
(isLink ? "text-orange-500 underline" : "");
return <Text className={className}>{children}</Text>;
};
export default React.forwardRef<any, ExtendedTextProps>(
(props, ref): React.ReactElement => {
const {
position,
color,
size,
weight,
isLink,
className,
children,
...rest
} = props;
export default CustomText;
const buildClassName = () => {
const textSize = toTextSize(size ?? "lg");
const textColor = toTextColor(color ?? "black");
const textWeight = toTextWeight(weight ?? "normal");
const textPosition = toTextPosition(position ?? "left");
return (
textSize +
" " +
textPosition +
" " +
textColor +
" " +
textWeight +
" " +
(isLink ? "text-orange-500 underline" : "") +
" " +
className
);
};
return (
<Text className={buildClassName()} {...ref} {...rest}>
{children}
</Text>
);
}
);

29
package-lock.json generated

@ -33,6 +33,7 @@
"nativewind": "^4.1.23",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-hook-form": "^7.54.2",
"react-native": "0.76.6",
"react-native-gesture-handler": "^2.20.2",
"react-native-gifted-charts": "^1.4.54",
@ -43,7 +44,8 @@
"react-native-vector-icons": "^10.2.0",
"react-native-web": "^0.19.13",
"react-native-webview": "13.12.5",
"tailwindcss": "^3.4.17"
"tailwindcss": "^3.4.17",
"validator": "^13.12.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
@ -12095,6 +12097,22 @@
"react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-hook-form": {
"version": "7.54.2",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz",
"integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-hook-form"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18 || ^19"
}
},
"node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
@ -14732,6 +14750,15 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/validator": {
"version": "13.12.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz",
"integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

@ -40,6 +40,7 @@
"nativewind": "^4.1.23",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-hook-form": "^7.54.2",
"react-native": "0.76.6",
"react-native-gesture-handler": "^2.20.2",
"react-native-gifted-charts": "^1.4.54",
@ -50,7 +51,8 @@
"react-native-vector-icons": "^10.2.0",
"react-native-web": "^0.19.13",
"react-native-webview": "13.12.5",
"tailwindcss": "^3.4.17"
"tailwindcss": "^3.4.17",
"validator": "^13.12.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",

@ -1,19 +0,0 @@
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.ElementRef<typeof View>, IBoxProps>(
({ className, ...props }, ref) => {
return (
<View ref={ref} {...props} className={boxStyle({ class: className })} />
);
}
);
Box.displayName = "Box";
export { Box };

@ -1,18 +0,0 @@
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>(
({ className, ...props }, ref) => {
return (
<div ref={ref} className={boxStyle({ class: className })} {...props} />
);
}
);
Box.displayName = 'Box';
export { Box };

@ -1,10 +0,0 @@
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,
});

@ -1,432 +0,0 @@
"use client";
import React = require("react");
import { ActivityIndicator, Pressable, Text, View } from "react-native";
const SCOPE = "BUTTON";
const Root = withStyleContext(Pressable, SCOPE);
const UIButton = createButton({
Root: Root,
Text,
Group: View,
Spinner: ActivityIndicator,
Icon: UIIcon,
});
cssInterop(PrimitiveIcon, {
className: {
target: "style",
nativeStyleToProp: {
height: true,
width: true,
fill: true,
color: "classNameColor",
stroke: true,
},
},
});
const buttonStyle = tva({
base: "group/button rounded-3xl bg-primary-500 flex-row items-center justify-center data-[focus-visible=true]:web:outline-none data-[focus-visible=true]:web:ring-2 data-[disabled=true]:opacity-40 gap-2",
variants: {
action: {
primary:
"bg-primary-500 data-[hover=true]:bg-primary-600 data-[active=true]:bg-primary-700 border-primary-300 data-[hover=true]:border-primary-400 data-[active=true]:border-primary-500 data-[focus-visible=true]:web:ring-indicator-info",
secondary:
"bg-secondary-500 border-secondary-300 data-[hover=true]:bg-secondary-600 data-[hover=true]:border-secondary-400 data-[active=true]:bg-secondary-700 data-[active=true]:border-secondary-700 data-[focus-visible=true]:web:ring-indicator-info",
positive:
"bg-success-500 border-success-300 data-[hover=true]:bg-success-600 data-[hover=true]:border-success-400 data-[active=true]:bg-success-700 data-[active=true]:border-success-500 data-[focus-visible=true]:web:ring-indicator-info",
negative:
"bg-error-500 border-error-300 data-[hover=true]:bg-error-600 data-[hover=true]:border-error-400 data-[active=true]:bg-error-700 data-[active=true]:border-error-500 data-[focus-visible=true]:web:ring-indicator-info",
default:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
variant: {
link: "px-0",
outline:
"bg-transparent border data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
solid: "",
},
size: {
xs: "h-8",
sm: "h-9",
md: "h-10",
lg: "h-11",
xl: "p-4 h-16",
"2xl": "p-6 h-20",
"3xl": "p-8 h-24",
},
},
compoundVariants: [
{
action: "primary",
variant: "link",
class:
"px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent",
},
{
action: "secondary",
variant: "link",
class:
"px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent",
},
{
action: "positive",
variant: "link",
class:
"px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent",
},
{
action: "negative",
variant: "link",
class:
"px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent",
},
{
action: "primary",
variant: "outline",
class:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
{
action: "secondary",
variant: "outline",
class:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
{
action: "positive",
variant: "outline",
class:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
{
action: "negative",
variant: "outline",
class:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
],
});
const buttonTextStyle = tva({
base: "text-typography-0 font-semibold web:select-none",
parentVariants: {
action: {
primary:
"text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700",
secondary:
"text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700",
positive:
"text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700",
negative:
"text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700",
},
variant: {
link: "underline data-[hover=true]:underline data-[active=true]:underline text-orange-400",
outline: "",
solid:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
size: {
xs: "text-xs",
sm: "text-sm",
md: "text-base",
lg: "text-lg",
xl: "text-xl",
"2xl": "text-2xl",
"3xl": "text-3xl",
},
},
parentCompoundVariants: [
{
variant: "solid",
action: "primary",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "solid",
action: "secondary",
class:
"text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800",
},
{
variant: "solid",
action: "positive",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "solid",
action: "negative",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "outline",
action: "primary",
class:
"text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500",
},
{
variant: "outline",
action: "secondary",
class:
"text-typography-500 data-[hover=true]:text-primary-600 data-[active=true]:text-typography-700",
},
{
variant: "outline",
action: "positive",
class:
"text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500",
},
{
variant: "outline",
action: "negative",
class:
"text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500",
},
],
});
const buttonIconStyle = tva({
base: "fill-none",
parentVariants: {
variant: {
link: "data-[hover=true]:underline data-[active=true]:underline",
outline: "",
solid:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
size: {
xs: "h-3.5 w-3.5",
sm: "h-4 w-4",
md: "h-[18px] w-[18px]",
lg: "h-[18px] w-[18px]",
xl: "h-5 w-5",
"2xl": "h-8 w-8",
"3xl": "h-10 w-10",
},
action: {
primary:
"text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700",
secondary:
"text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700",
positive:
"text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700",
negative:
"text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700",
},
},
parentCompoundVariants: [
{
variant: "solid",
action: "primary",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "solid",
action: "secondary",
class:
"text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800",
},
{
variant: "solid",
action: "positive",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "solid",
action: "negative",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
],
});
const buttonGroupStyle = tva({
base: "",
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",
},
isAttached: {
true: "gap-0",
},
flexDirection: {
row: "flex-row",
column: "flex-col",
"row-reverse": "flex-row-reverse",
"column-reverse": "flex-col-reverse",
},
},
});
type IButtonProps = Omit<
React.ComponentPropsWithoutRef<typeof UIButton>,
"context"
> &
VariantProps<typeof buttonStyle> & { className?: string };
const Button = React.forwardRef<
React.ElementRef<typeof UIButton>,
IButtonProps
>(
(
{ className, variant = "solid", size = "md", action = "primary", ...props },
ref
) => {
return (
<UIButton
ref={ref}
{...props}
className={buttonStyle({ variant, size, action, class: className })}
context={{ variant, size, action }}
/>
);
}
);
type IButtonTextProps = React.ComponentPropsWithoutRef<typeof UIButton.Text> &
VariantProps<typeof buttonTextStyle> & { className?: string };
const ButtonText = React.forwardRef<
React.ElementRef<typeof UIButton.Text>,
IButtonTextProps
>(({ className, variant, size, action, ...props }, ref) => {
const {
variant: parentVariant,
size: parentSize,
action: parentAction,
} = useStyleContext(SCOPE);
return (
<UIButton.Text
ref={ref}
{...props}
className={buttonTextStyle({
parentVariants: {
variant: parentVariant,
size: parentSize,
action: parentAction,
},
variant,
size,
action,
class: className,
})}
/>
);
});
const ButtonSpinner = UIButton.Spinner;
type IButtonIcon = React.ComponentPropsWithoutRef<typeof UIButton.Icon> &
VariantProps<typeof buttonIconStyle> & {
className?: string | undefined;
as?: React.ElementType;
height?: number;
width?: number;
};
const ButtonIcon = React.forwardRef<
React.ElementRef<typeof UIButton.Icon>,
IButtonIcon
>(({ className, size, ...props }, ref) => {
const {
variant: parentVariant,
size: parentSize,
action: parentAction,
} = useStyleContext(SCOPE);
if (typeof size === "number") {
return (
<UIButton.Icon
ref={ref}
{...props}
className={buttonIconStyle({ class: className })}
size={size}
/>
);
} else if (
(props.height !== undefined || props.width !== undefined) &&
size === undefined
) {
return (
<UIButton.Icon
ref={ref}
{...props}
className={buttonIconStyle({ class: className })}
/>
);
}
return (
<UIButton.Icon
{...props}
className={buttonIconStyle({
parentVariants: {
size: parentSize,
variant: parentVariant,
action: parentAction,
},
size,
class: className,
})}
ref={ref}
/>
);
});
type IButtonGroupProps = React.ComponentPropsWithoutRef<typeof UIButton.Group> &
VariantProps<typeof buttonGroupStyle>;
const ButtonGroup = React.forwardRef<
React.ElementRef<typeof UIButton.Group>,
IButtonGroupProps
>(
(
{
className,
space = "md",
isAttached = false,
flexDirection = "column",
...props
},
ref
) => {
return (
<UIButton.Group
className={buttonGroupStyle({
class: className,
space,
isAttached,
flexDirection,
})}
{...props}
ref={ref}
/>
);
}
);
Button.displayName = "Button";
ButtonText.displayName = "ButtonText";
ButtonSpinner.displayName = "ButtonSpinner";
ButtonIcon.displayName = "ButtonIcon";
ButtonGroup.displayName = "ButtonGroup";
export { Button, ButtonText, ButtonSpinner, ButtonIcon, ButtonGroup };
export type ButtonActions = keyof typeof buttonStyle.variants.action;

@ -1,23 +0,0 @@
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.ElementRef<typeof View>, ICardProps>(
({ className, size = 'md', variant = 'elevated', ...props }, ref) => {
return (
<View
className={cardStyle({ size, variant, class: className })}
{...props}
ref={ref}
/>
);
}
);
Card.displayName = 'Card';
export { Card };

@ -1,22 +0,0 @@
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>(
({ className, size = 'md', variant = 'elevated', ...props }, ref) => {
return (
<div
className={cardStyle({ size, variant, class: className })}
{...props}
ref={ref}
/>
);
}
);
Card.displayName = 'Card';
export { Card };

@ -1,20 +0,0 @@
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',
},
},
});

@ -1,468 +0,0 @@
'use client';
import { Text, View } from 'react-native';
import React from 'react';
import { createFormControl } from '@gluestack-ui/form-control';
import { tva } from '@gluestack-ui/nativewind-utils/tva';
import {
withStyleContext,
useStyleContext,
} from '@gluestack-ui/nativewind-utils/withStyleContext';
import { cssInterop } from 'nativewind';
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
import { PrimitiveIcon, UIIcon } from '@gluestack-ui/icon';
const SCOPE = 'FORM_CONTROL';
const formControlStyle = tva({
base: 'flex flex-col',
variants: {
size: {
sm: '',
md: '',
lg: '',
},
},
});
const formControlErrorIconStyle = tva({
base: 'text-error-700 fill-none',
variants: {
size: {
'2xs': 'h-3 w-3',
'xs': 'h-3.5 w-3.5',
'sm': 'h-4 w-4',
'md': 'h-[18px] w-[18px]',
'lg': 'h-5 w-5',
'xl': 'h-6 w-6',
},
},
});
const formControlErrorStyle = tva({
base: 'flex flex-row justify-start items-center mt-1 gap-1',
});
const formControlErrorTextStyle = tva({
base: 'text-error-700',
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',
},
},
});
const formControlHelperStyle = tva({
base: 'flex flex-row justify-start items-center mt-1',
});
const formControlHelperTextStyle = tva({
base: 'text-typography-500',
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-xs',
'md': 'text-sm',
'lg': 'text-base',
'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',
},
},
});
const formControlLabelStyle = tva({
base: 'flex flex-row justify-start items-center mb-1',
});
const formControlLabelTextStyle = tva({
base: 'font-medium text-typography-900',
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',
},
},
});
const formControlLabelAstrickStyle = tva({
base: 'font-medium text-typography-900',
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',
},
},
});
type IFormControlLabelAstrickProps = React.ComponentPropsWithoutRef<
typeof Text
> &
VariantProps<typeof formControlLabelAstrickStyle>;
const FormControlLabelAstrick = React.forwardRef<
React.ElementRef<typeof Text>,
IFormControlLabelAstrickProps
>(({ className, ...props }, ref) => {
const { size: parentSize } = useStyleContext(SCOPE);
return (
<Text
ref={ref}
className={formControlLabelAstrickStyle({
parentVariants: { size: parentSize },
class: className,
})}
{...props}
/>
);
});
export const UIFormControl = createFormControl({
Root: withStyleContext(View, SCOPE),
Error: View,
ErrorText: Text,
ErrorIcon: UIIcon,
Label: View,
LabelText: Text,
LabelAstrick: FormControlLabelAstrick,
Helper: View,
HelperText: Text,
});
cssInterop(PrimitiveIcon, {
className: {
target: 'style',
nativeStyleToProp: {
height: true,
width: true,
fill: true,
color: true,
stroke: true,
},
},
});
type IFormControlProps = React.ComponentProps<typeof UIFormControl> &
VariantProps<typeof formControlStyle>;
const FormControl = React.forwardRef<
React.ElementRef<typeof UIFormControl>,
IFormControlProps
>(({ className, size = 'md', ...props }, ref) => {
return (
<UIFormControl
ref={ref}
className={formControlStyle({ size, class: className })}
{...props}
context={{ size }}
/>
);
});
type IFormControlErrorProps = React.ComponentProps<typeof UIFormControl.Error> &
VariantProps<typeof formControlErrorStyle>;
const FormControlError = React.forwardRef<
React.ElementRef<typeof UIFormControl.Error>,
IFormControlErrorProps
>(({ className, ...props }, ref) => {
return (
<UIFormControl.Error
ref={ref}
className={formControlErrorStyle({ class: className })}
{...props}
/>
);
});
type IFormControlErrorTextProps = React.ComponentProps<
typeof UIFormControl.Error.Text
> &
VariantProps<typeof formControlErrorTextStyle>;
const FormControlErrorText = React.forwardRef<
React.ElementRef<typeof UIFormControl.Error.Text>,
IFormControlErrorTextProps
>(({ className, size, ...props }, ref) => {
const { size: parentSize } = useStyleContext(SCOPE);
return (
<UIFormControl.Error.Text
className={formControlErrorTextStyle({
parentVariants: { size: parentSize },
size,
class: className,
})}
ref={ref}
{...props}
/>
);
});
type IFormControlErrorIconProps = React.ComponentProps<
typeof UIFormControl.Error.Icon
> &
VariantProps<typeof formControlErrorIconStyle> & {
height?: number;
width?: number;
};
const FormControlErrorIcon = React.forwardRef<
React.ElementRef<typeof UIFormControl.Error.Icon>,
IFormControlErrorIconProps
>(({ className, size, ...props }, ref) => {
const { size: parentSize } = useStyleContext(SCOPE);
if (typeof size === 'number') {
return (
<UIFormControl.Error.Icon
ref={ref}
{...props}
className={formControlErrorIconStyle({ class: className })}
size={size}
/>
);
} else if (
(props.height !== undefined || props.width !== undefined) &&
size === undefined
) {
return (
<UIFormControl.Error.Icon
ref={ref}
{...props}
className={formControlErrorIconStyle({ class: className })}
/>
);
}
return (
<UIFormControl.Error.Icon
className={formControlErrorIconStyle({
parentVariants: { size: parentSize },
size,
class: className,
})}
{...props}
/>
);
});
type IFormControlLabelProps = React.ComponentProps<typeof UIFormControl.Label> &
VariantProps<typeof formControlLabelStyle>;
const FormControlLabel = React.forwardRef<
React.ElementRef<typeof UIFormControl.Label>,
IFormControlLabelProps
>(({ className, ...props }, ref) => {
return (
<UIFormControl.Label
ref={ref}
className={formControlLabelStyle({ class: className })}
{...props}
/>
);
});
type IFormControlLabelTextProps = React.ComponentProps<
typeof UIFormControl.Label.Text
> &
VariantProps<typeof formControlLabelTextStyle>;
const FormControlLabelText = React.forwardRef<
React.ElementRef<typeof UIFormControl.Label.Text>,
IFormControlLabelTextProps
>(({ className, size, ...props }, ref) => {
const { size: parentSize } = useStyleContext(SCOPE);
return (
<UIFormControl.Label.Text
className={formControlLabelTextStyle({
parentVariants: { size: parentSize },
size,
class: className,
})}
ref={ref}
{...props}
/>
);
});
type IFormControlHelperProps = React.ComponentProps<
typeof UIFormControl.Helper
> &
VariantProps<typeof formControlHelperStyle>;
const FormControlHelper = React.forwardRef<
React.ElementRef<typeof UIFormControl.Helper>,
IFormControlHelperProps
>(({ className, ...props }, ref) => {
return (
<UIFormControl.Helper
ref={ref}
className={formControlHelperStyle({
class: className,
})}
{...props}
/>
);
});
type IFormControlHelperTextProps = React.ComponentProps<
typeof UIFormControl.Helper.Text
> &
VariantProps<typeof formControlHelperTextStyle>;
const FormControlHelperText = React.forwardRef<
React.ElementRef<typeof UIFormControl.Helper.Text>,
IFormControlHelperTextProps
>(({ className, size, ...props }, ref) => {
const { size: parentSize } = useStyleContext(SCOPE);
return (
<UIFormControl.Helper.Text
className={formControlHelperTextStyle({
parentVariants: { size: parentSize },
size,
class: className,
})}
ref={ref}
{...props}
/>
);
});
FormControl.displayName = 'FormControl';
FormControlError.displayName = 'FormControlError';
FormControlErrorText.displayName = 'FormControlErrorText';
FormControlErrorIcon.displayName = 'FormControlErrorIcon';
FormControlLabel.displayName = 'FormControlLabel';
FormControlLabelText.displayName = 'FormControlLabelText';
FormControlLabelAstrick.displayName = 'FormControlLabelAstrick';
FormControlHelper.displayName = 'FormControlHelper';
FormControlHelperText.displayName = 'FormControlHelperText';
export {
FormControl,
FormControlError,
FormControlErrorText,
FormControlErrorIcon,
FormControlLabel,
FormControlLabelText,
FormControlLabelAstrick,
FormControlHelper,
FormControlHelperText,
};

@ -1,309 +0,0 @@
"use client";
import { vars } from "nativewind";
export const config = {
light: vars({
"--color-primary-0": "179 179 179",
"--color-primary-50": "153 153 153",
"--color-primary-100": "128 128 128",
"--color-primary-200": "115 115 115",
"--color-primary-300": "102 102 102",
"--color-primary-400": "82 82 82",
"--color-primary-500": "51 51 51",
"--color-primary-600": "41 41 41",
"--color-primary-700": "31 31 31",
"--color-primary-800": "13 13 13",
"--color-primary-900": "10 10 10",
"--color-primary-950": "8 8 8",
/* Secondary */
"--color-secondary-0": "253 253 253",
"--color-secondary-50": "251 251 251",
"--color-secondary-100": "246 246 246",
"--color-secondary-200": "242 242 242",
"--color-secondary-300": "237 237 237",
"--color-secondary-400": "230 230 231",
"--color-secondary-500": "217 217 219",
"--color-secondary-600": "198 199 199",
"--color-secondary-700": "189 189 189",
"--color-secondary-800": "177 177 177",
"--color-secondary-900": "165 164 164",
"--color-secondary-950": "157 157 157",
/* Tertiary */
"--color-tertiary-0": "255 250 245",
"--color-tertiary-50": "255 242 229",
"--color-tertiary-100": "255 233 213",
"--color-tertiary-200": "254 209 170",
"--color-tertiary-300": "253 180 116",
"--color-tertiary-400": "251 157 75",
"--color-tertiary-500": "231 129 40",
"--color-tertiary-600": "215 117 31",
"--color-tertiary-700": "180 98 26",
"--color-tertiary-800": "130 73 23",
"--color-tertiary-900": "108 61 19",
"--color-tertiary-950": "84 49 18",
/* Error */
"--color-error-0": "254 233 233",
"--color-error-50": "254 226 226",
"--color-error-100": "254 202 202",
"--color-error-200": "252 165 165",
"--color-error-300": "248 113 113",
"--color-error-400": "239 68 68",
"--color-error-500": "230 53 53",
"--color-error-600": "220 38 38",
"--color-error-700": "185 28 28",
"--color-error-800": "153 27 27",
"--color-error-900": "127 29 29",
"--color-error-950": "83 19 19",
/* Success */
"--color-success-0": "228 255 244",
"--color-success-50": "202 255 232",
"--color-success-100": "162 241 192",
"--color-success-200": "132 211 162",
"--color-success-300": "102 181 132",
"--color-success-400": "72 151 102",
"--color-success-500": "52 131 82",
"--color-success-600": "42 121 72",
"--color-success-700": "32 111 62",
"--color-success-800": "22 101 52",
"--color-success-900": "20 83 45",
"--color-success-950": "27 50 36",
/* Warning */
"--color-warning-0": "255 249 245",
"--color-warning-50": "255 244 236",
"--color-warning-100": "255 231 213",
"--color-warning-200": "254 205 170",
"--color-warning-300": "253 173 116",
"--color-warning-400": "251 149 75",
"--color-warning-500": "231 120 40",
"--color-warning-600": "215 108 31",
"--color-warning-700": "180 90 26",
"--color-warning-800": "130 68 23",
"--color-warning-900": "108 56 19",
"--color-warning-950": "84 45 18",
/* Info */
"--color-info-0": "236 248 254",
"--color-info-50": "199 235 252",
"--color-info-100": "162 221 250",
"--color-info-200": "124 207 248",
"--color-info-300": "87 194 246",
"--color-info-400": "50 180 244",
"--color-info-500": "13 166 242",
"--color-info-600": "11 141 205",
"--color-info-700": "9 115 168",
"--color-info-800": "7 90 131",
"--color-info-900": "5 64 93",
"--color-info-950": "3 38 56",
/* Typography */
"--color-typography-0": "254 254 255",
"--color-typography-50": "245 245 245",
"--color-typography-100": "229 229 229",
"--color-typography-200": "219 219 220",
"--color-typography-300": "212 212 212",
"--color-typography-400": "163 163 163",
"--color-typography-500": "140 140 140",
"--color-typography-600": "115 115 115",
"--color-typography-700": "82 82 82",
"--color-typography-800": "64 64 64",
"--color-typography-900": "38 38 39",
"--color-typography-950": "23 23 23",
/* Outline */
"--color-outline-0": "253 254 254",
"--color-outline-50": "243 243 243",
"--color-outline-100": "230 230 230",
"--color-outline-200": "221 220 219",
"--color-outline-300": "211 211 211",
"--color-outline-400": "165 163 163",
"--color-outline-500": "140 141 141",
"--color-outline-600": "115 116 116",
"--color-outline-700": "83 82 82",
"--color-outline-800": "65 65 65",
"--color-outline-900": "39 38 36",
"--color-outline-950": "26 23 23",
/* Background */
"--color-background-0": "255 255 255",
"--color-background-50": "246 246 246",
"--color-background-100": "242 241 241",
"--color-background-200": "220 219 219",
"--color-background-300": "213 212 212",
"--color-background-400": "162 163 163",
"--color-background-500": "142 142 142",
"--color-background-600": "116 116 116",
"--color-background-700": "83 82 82",
"--color-background-800": "65 64 64",
"--color-background-900": "39 38 37",
"--color-background-950": "18 18 18",
/* Background Special */
"--color-background-error": "254 241 241",
"--color-background-warning": "255 243 234",
"--color-background-success": "237 252 242",
"--color-background-muted": "247 248 247",
"--color-background-info": "235 248 254",
/* Focus Ring Indicator */
"--color-indicator-primary": "55 55 55",
"--color-indicator-info": "83 153 236",
"--color-indicator-error": "185 28 28",
}),
dark: vars({
"--color-primary-0": "166 166 166",
"--color-primary-50": "175 175 175",
"--color-primary-100": "186 186 186",
"--color-primary-200": "197 197 197",
"--color-primary-300": "212 212 212",
"--color-primary-400": "221 221 221",
"--color-primary-500": "230 230 230",
"--color-primary-600": "240 240 240",
"--color-primary-700": "250 250 250",
"--color-primary-800": "253 253 253",
"--color-primary-900": "254 249 249",
"--color-primary-950": "253 252 252",
/* Secondary */
"--color-secondary-0": "20 20 20",
"--color-secondary-50": "23 23 23",
"--color-secondary-100": "31 31 31",
"--color-secondary-200": "39 39 39",
"--color-secondary-300": "44 44 44",
"--color-secondary-400": "56 57 57",
"--color-secondary-500": "63 64 64",
"--color-secondary-600": "86 86 86",
"--color-secondary-700": "110 110 110",
"--color-secondary-800": "135 135 135",
"--color-secondary-900": "150 150 150",
"--color-secondary-950": "164 164 164",
/* Tertiary */
"--color-tertiary-0": "84 49 18",
"--color-tertiary-50": "108 61 19",
"--color-tertiary-100": "130 73 23",
"--color-tertiary-200": "180 98 26",
"--color-tertiary-300": "215 117 31",
"--color-tertiary-400": "231 129 40",
"--color-tertiary-500": "251 157 75",
"--color-tertiary-600": "253 180 116",
"--color-tertiary-700": "254 209 170",
"--color-tertiary-800": "255 233 213",
"--color-tertiary-900": "255 242 229",
"--color-tertiary-950": "255 250 245",
/* Error */
"--color-error-0": "83 19 19",
"--color-error-50": "127 29 29",
"--color-error-100": "153 27 27",
"--color-error-200": "185 28 28",
"--color-error-300": "220 38 38",
"--color-error-400": "230 53 53",
"--color-error-500": "239 68 68",
"--color-error-600": "249 97 96",
"--color-error-700": "229 91 90",
"--color-error-800": "254 202 202",
"--color-error-900": "254 226 226",
"--color-error-950": "254 233 233",
/* Success */
"--color-success-0": "27 50 36",
"--color-success-50": "20 83 45",
"--color-success-100": "22 101 52",
"--color-success-200": "32 111 62",
"--color-success-300": "42 121 72",
"--color-success-400": "52 131 82",
"--color-success-500": "72 151 102",
"--color-success-600": "102 181 132",
"--color-success-700": "132 211 162",
"--color-success-800": "162 241 192",
"--color-success-900": "202 255 232",
"--color-success-950": "228 255 244",
/* Warning */
"--color-warning-0": "84 45 18",
"--color-warning-50": "108 56 19",
"--color-warning-100": "130 68 23",
"--color-warning-200": "180 90 26",
"--color-warning-300": "215 108 31",
"--color-warning-400": "231 120 40",
"--color-warning-500": "251 149 75",
"--color-warning-600": "253 173 116",
"--color-warning-700": "254 205 170",
"--color-warning-800": "255 231 213",
"--color-warning-900": "255 244 237",
"--color-warning-950": "255 249 245",
/* Info */
"--color-info-0": "3 38 56",
"--color-info-50": "5 64 93",
"--color-info-100": "7 90 131",
"--color-info-200": "9 115 168",
"--color-info-300": "11 141 205",
"--color-info-400": "13 166 242",
"--color-info-500": "50 180 244",
"--color-info-600": "87 194 246",
"--color-info-700": "124 207 248",
"--color-info-800": "162 221 250",
"--color-info-900": "199 235 252",
"--color-info-950": "236 248 254",
/* Typography */
"--color-typography-0": "23 23 23",
"--color-typography-50": "38 38 39",
"--color-typography-100": "64 64 64",
"--color-typography-200": "82 82 82",
"--color-typography-300": "115 115 115",
"--color-typography-400": "140 140 140",
"--color-typography-500": "163 163 163",
"--color-typography-600": "212 212 212",
"--color-typography-700": "219 219 220",
"--color-typography-800": "229 229 229",
"--color-typography-900": "245 245 245",
"--color-typography-950": "254 254 255",
/* Outline */
"--color-outline-0": "26 23 23",
"--color-outline-50": "39 38 36",
"--color-outline-100": "65 65 65",
"--color-outline-200": "83 82 82",
"--color-outline-300": "115 116 116",
"--color-outline-400": "140 141 141",
"--color-outline-500": "165 163 163",
"--color-outline-600": "211 211 211",
"--color-outline-700": "221 220 219",
"--color-outline-800": "230 230 230",
"--color-outline-900": "243 243 243",
"--color-outline-950": "253 254 254",
/* Background */
"--color-background-0": "18 18 18",
"--color-background-50": "39 38 37",
"--color-background-100": "65 64 64",
"--color-background-200": "83 82 82",
"--color-background-300": "116 116 116",
"--color-background-400": "142 142 142",
"--color-background-500": "162 163 163",
"--color-background-600": "213 212 212",
"--color-background-700": "229 228 228",
"--color-background-800": "242 241 241",
"--color-background-900": "246 246 246",
"--color-background-950": "255 255 255",
/* Background Special */
"--color-background-error": "66 43 43",
"--color-background-warning": "65 47 35",
"--color-background-success": "28 43 33",
"--color-background-muted": "51 51 51",
"--color-background-info": "26 40 46",
/* Focus Ring Indicator */
"--color-indicator-primary": "247 247 247",
"--color-indicator-info": "161 199 245",
"--color-indicator-error": "232 70 69",
}),
};

@ -1,48 +0,0 @@
import React from "react";
import { config } from "./config";
import { ColorSchemeName, useColorScheme, View, ViewProps } from "react-native";
import { OverlayProvider } from "@gluestack-ui/overlay";
import { ToastProvider } from "@gluestack-ui/toast";
import { colorScheme as colorSchemeNW } from "nativewind";
type ModeType = "light" | "dark" | "system";
const getColorSchemeName = (
colorScheme: ColorSchemeName,
mode: ModeType
): "light" | "dark" => {
if (mode === "system") {
return colorScheme ?? "light";
}
return mode;
};
export function GluestackUIProvider({
mode = "light",
...props
}: {
mode?: "light" | "dark" | "system";
children?: React.ReactNode;
style?: ViewProps["style"];
}) {
const colorScheme = useColorScheme();
const colorSchemeName = getColorSchemeName(colorScheme, mode);
colorSchemeNW.set(mode);
return (
<View
style={[
config[colorSchemeName],
// eslint-disable-next-line react-native/no-inline-styles
{ flex: 1, height: "100%", width: "100%" },
props.style,
]}
>
<OverlayProvider>
<ToastProvider>{props.children}</ToastProvider>
</OverlayProvider>
</View>
);
}

@ -1,219 +0,0 @@
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.ElementRef<typeof H1>, IHeadingProps>(
(
{
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
ref={ref}
/>
);
case '2xl':
return (
<H2
className={headingStyle({
size,
isTruncated,
bold,
underline,
strikeThrough,
sub,
italic,
highlight,
class: className,
})}
{...props}
// @ts-expect-error
ref={ref}
/>
);
case 'xl':
return (
<H3
className={headingStyle({
size,
isTruncated,
bold,
underline,
strikeThrough,
sub,
italic,
highlight,
class: className,
})}
{...props}
// @ts-expect-error
ref={ref}
/>
);
case 'lg':
return (
<H4
className={headingStyle({
size,
isTruncated,
bold,
underline,
strikeThrough,
sub,
italic,
highlight,
class: className,
})}
{...props}
// @ts-expect-error
ref={ref}
/>
);
case 'md':
return (
<H5
className={headingStyle({
size,
isTruncated,
bold,
underline,
strikeThrough,
sub,
italic,
highlight,
class: className,
})}
{...props}
// @ts-expect-error
ref={ref}
/>
);
case 'sm':
case 'xs':
return (
<H6
className={headingStyle({
size,
isTruncated,
bold,
underline,
strikeThrough,
sub,
italic,
highlight,
class: className,
})}
{...props}
// @ts-expect-error
ref={ref}
/>
);
default:
return (
<H4
className={headingStyle({
size,
isTruncated,
bold,
underline,
strikeThrough,
sub,
italic,
highlight,
class: className,
})}
{...props}
// @ts-expect-error
ref={ref}
/>
);
}
}
)
);
const Heading = memo(
forwardRef<React.ElementRef<typeof H1>, IHeadingProps>(
({ 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 };

@ -1,203 +0,0 @@
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>(
(
{
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>(
({ 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 };

@ -1,43 +0,0 @@
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",
},
},
});

@ -1,23 +0,0 @@
import React from 'react';
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
import { View } from 'react-native';
import type { ViewProps } from 'react-native';
import { hstackStyle } from './styles';
type IHStackProps = ViewProps & VariantProps<typeof hstackStyle>;
const HStack = React.forwardRef<React.ElementRef<typeof View>, IHStackProps>(
({ className, space, reversed, ...props }, ref) => {
return (
<View
className={hstackStyle({ space, reversed, class: className })}
{...props}
ref={ref}
/>
);
}
);
HStack.displayName = 'HStack';
export { HStack };

@ -1,22 +0,0 @@
import React from 'react';
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
import { hstackStyle } from './styles';
type IHStackProps = React.ComponentPropsWithoutRef<'div'> &
VariantProps<typeof hstackStyle>;
const HStack = React.forwardRef<React.ElementRef<'div'>, IHStackProps>(
({ className, space, reversed, ...props }, ref) => {
return (
<div
className={hstackStyle({ space, reversed, class: className })}
{...props}
ref={ref}
/>
);
}
);
HStack.displayName = 'HStack';
export { HStack };

@ -1,25 +0,0 @@
import { isWeb } from "@gluestack-ui/nativewind-utils/IsWeb";
import { tva } from "@gluestack-ui/nativewind-utils/tva";
const baseStyle = isWeb
? "flex 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 hstackStyle = tva({
base: `flex-row ${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-row-reverse",
},
},
});

@ -1,214 +0,0 @@
"use client";
import React from "react";
import { createInput } from "@gluestack-ui/input";
import { View, Pressable, TextInput } from "react-native";
import { tva } from "@gluestack-ui/nativewind-utils/tva";
import {
withStyleContext,
useStyleContext,
} from "@gluestack-ui/nativewind-utils/withStyleContext";
import { cssInterop } from "nativewind";
import type { VariantProps } from "@gluestack-ui/nativewind-utils";
import { PrimitiveIcon, UIIcon } from "@gluestack-ui/icon";
const SCOPE = "INPUT";
const UIInput = createInput({
Root: withStyleContext(View, SCOPE),
Icon: UIIcon,
Slot: Pressable,
Input: TextInput,
});
cssInterop(PrimitiveIcon, {
className: {
target: "style",
nativeStyleToProp: {
height: true,
width: true,
fill: true,
color: "classNameColor",
stroke: true,
},
},
});
const inputStyle = tva({
base: "border-background-300 flex-row overflow-hidden content-center data-[hover=true]:border-outline-400 data-[focus=true]:border-orange-400 data-[focus=true]:hover:border-primary-700 data-[disabled=true]:opacity-40 data-[disabled=true]:hover:border-background-300 items-center",
variants: {
size: {
xl: "px-4 h-16",
lg: "px-4 h-11",
md: "px-4 h-10",
sm: "px-4 h-9",
},
variant: {
underlined:
"rounded-none border-b data-[invalid=true]:border-b-2 data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700",
outline:
"rounded-3xl border data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-indicator-primary data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-indicator-error data-[invalid=true]:data-[focus=true]:hover:web:ring-1 data-[invalid=true]:data-[focus=true]:hover:web:ring-inset data-[invalid=true]:data-[focus=true]:hover:web:ring-indicator-error data-[invalid=true]:data-[disabled=true]:hover:web:ring-1 data-[invalid=true]:data-[disabled=true]:hover:web:ring-inset data-[invalid=true]:data-[disabled=true]:hover:web:ring-indicator-error",
rounded:
"rounded-full border data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-indicator-primary data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-indicator-error data-[invalid=true]:data-[focus=true]:hover:web:ring-1 data-[invalid=true]:data-[focus=true]:hover:web:ring-inset data-[invalid=true]:data-[focus=true]:hover:web:ring-indicator-error data-[invalid=true]:data-[disabled=true]:hover:web:ring-1 data-[invalid=true]:data-[disabled=true]:hover:web:ring-inset data-[invalid=true]:data-[disabled=true]:hover:web:ring-indicator-error",
},
},
});
const inputIconStyle = tva({
base: "justify-center items-center text-typography-400 fill-none",
parentVariants: {
size: {
"2xs": "h-3 w-3",
xs: "h-3.5 w-3.5",
sm: "h-4 w-4",
md: "h-[18px] w-[18px]",
lg: "h-5 w-5",
xl: "h-6 w-6",
},
},
});
const inputSlotStyle = tva({
base: "justify-center items-center web:disabled:cursor-not-allowed",
});
const inputFieldStyle = tva({
base: "flex-1 text-typography-900 py-0 px-3 placeholder:text-typography-500 h-full ios:leading-[0px] web:cursor-text web:data-[disabled=true]:cursor-not-allowed",
parentVariants: {
variant: {
underlined: "web:outline-0 web:outline-none px-0",
outline: "web:outline-0 web:outline-none",
rounded: "web:outline-0 web:outline-none px-4",
},
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",
},
},
});
type IInputProps = React.ComponentProps<typeof UIInput> &
VariantProps<typeof inputStyle> & { className?: string };
const Input = React.forwardRef<React.ElementRef<typeof UIInput>, IInputProps>(
({ className, variant = "outline", size = "md", ...props }, ref) => {
return (
<UIInput
ref={ref}
{...props}
className={inputStyle({ variant, size, class: className })}
context={{ variant, size }}
/>
);
}
);
type IInputIconProps = React.ComponentProps<typeof UIInput.Icon> &
VariantProps<typeof inputIconStyle> & {
className?: string;
height?: number;
width?: number;
};
const InputIcon = React.forwardRef<
React.ElementRef<typeof UIInput.Icon>,
IInputIconProps
>(({ className, size, ...props }, ref) => {
const { size: parentSize } = useStyleContext(SCOPE);
if (typeof size === "number") {
return (
<UIInput.Icon
ref={ref}
{...props}
className={inputIconStyle({ class: className })}
size={size}
/>
);
} else if (
(props.height !== undefined || props.width !== undefined) &&
size === undefined
) {
return (
<UIInput.Icon
ref={ref}
{...props}
className={inputIconStyle({ class: className })}
/>
);
}
return (
<UIInput.Icon
ref={ref}
{...props}
className={inputIconStyle({
parentVariants: {
size: parentSize,
},
class: className,
})}
/>
);
});
type IInputSlotProps = React.ComponentProps<typeof UIInput.Slot> &
VariantProps<typeof inputSlotStyle> & { className?: string };
const InputSlot = React.forwardRef<
React.ElementRef<typeof UIInput.Slot>,
IInputSlotProps
>(({ className, ...props }, ref) => {
return (
<UIInput.Slot
ref={ref}
{...props}
className={inputSlotStyle({
class: className,
})}
/>
);
});
type IInputFieldProps = React.ComponentProps<typeof UIInput.Input> &
VariantProps<typeof inputFieldStyle> & { className?: string };
const InputField = React.forwardRef<
React.ElementRef<typeof UIInput.Input>,
IInputFieldProps
>(({ className, ...props }, ref) => {
const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE);
return (
<UIInput.Input
ref={ref}
{...props}
className={inputFieldStyle({
parentVariants: {
variant: parentVariant,
size: parentSize,
},
class: className,
})}
/>
);
});
Input.displayName = "Input";
InputIcon.displayName = "InputIcon";
InputSlot.displayName = "InputSlot";
InputField.displayName = "InputField";
export { Input, InputField, InputIcon, InputSlot };

@ -1,102 +0,0 @@
"use client";
import { createLink } from "@gluestack-ui/link";
import { Pressable } from "react-native";
import { Text } from "react-native";
import { tva } from "@gluestack-ui/nativewind-utils/tva";
import { withStyleContext } from "@gluestack-ui/nativewind-utils/withStyleContext";
import { cssInterop } from "nativewind";
import type { VariantProps } from "@gluestack-ui/nativewind-utils";
import React from "react";
export const UILink = createLink({
Root: withStyleContext(Pressable),
Text: Text,
});
cssInterop(UILink, { className: "style" });
cssInterop(UILink.Text, { className: "style" });
const linkStyle = tva({
base: "group/link web:outline-0 data-[disabled=true]:web:cursor-not-allowed data-[focus-visible=true]:web:ring-2 data-[focus-visible=true]:web:ring-indicator-primary data-[focus-visible=true]:web:outline-0 data-[disabled=true]:opacity-4 ",
});
const linkTextStyle = tva({
base: "underline text-orange-400 data-[hover=true]:text-orange-400 data-[hover=true]:no-underline data-[active=true]:text-orange-400 font-normal font-body web:font-sans web:tracking-sm web:my-0 web:bg-transparent web:border-0 web:box-border web:display-inline web:list-none web:margin-0 web:padding-0 web:position-relative web:text-start web:whitespace-pre-wrap web:word-wrap-break-word",
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",
},
},
});
type ILinkProps = React.ComponentProps<typeof UILink> &
VariantProps<typeof linkStyle> & { className?: string };
const Link = React.forwardRef<React.ElementRef<typeof UILink>, ILinkProps>(
({ className, ...props }, ref) => {
return (
<UILink
ref={ref}
{...props}
className={linkStyle({ class: className })}
/>
);
}
);
type ILinkTextProps = React.ComponentProps<typeof UILink.Text> &
VariantProps<typeof linkTextStyle> & { className?: string };
const LinkText = React.forwardRef<
React.ElementRef<typeof UILink.Text>,
ILinkTextProps
>(({ className, size = "md", ...props }, ref) => {
return (
<UILink.Text
ref={ref}
{...props}
className={linkTextStyle({
class: className,
size,
})}
/>
);
});
Link.displayName = "Link";
LinkText.displayName = "LinkText";
export { Link, LinkText };

@ -1,48 +0,0 @@
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.ElementRef<typeof RNText>, ITextProps>(
(
{
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 };

@ -1,45 +0,0 @@
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.ElementRef<'span'>, ITextProps>(
(
{
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 };

@ -1,47 +0,0 @@
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",
},
},
});

@ -1,24 +0,0 @@
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.ElementRef<typeof View>, IVStackProps>(
({ className, space, reversed, ...props }, ref) => {
return (
<View
className={vstackStyle({ space, reversed, class: className })}
{...props}
ref={ref}
/>
);
}
);
VStack.displayName = 'VStack';
export { VStack };

@ -1,23 +0,0 @@
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.ElementRef<'div'>, IVStackProps>(
({ className, space, reversed, ...props }, ref) => {
return (
<div
className={vstackStyle({ space, reversed, class: className })}
{...props}
ref={ref}
/>
);
}
);
VStack.displayName = 'VStack';
export { VStack };

@ -1,25 +0,0 @@
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',
},
},
});
Loading…
Cancel
Save