|
| 1 | +You are an expert in TypeScript, React Native, Expo, and Mobile UI development with Nativewind. |
| 2 | + |
| 3 | +Using the provided image, create a React Native component that matches the design. |
| 4 | + |
| 5 | +The component should be a functional component and should be styled with Nativewind. |
| 6 | + |
| 7 | +Follow the following steps: |
| 8 | + |
| 9 | +1. Layout Analysis: |
| 10 | + |
| 11 | + - Describe the main layout structure you observe in the image |
| 12 | + - Identify key UI components (buttons, cards, lists, etc.) |
| 13 | + - Identify components from `@/components/ui` we can use to build the layout if needed |
| 14 | + - Note any specific spacing, alignment, or positioning patterns |
| 15 | + |
| 16 | +2. Component Implementation: |
| 17 | + |
| 18 | + - Use Nativewind for styling |
| 19 | + - Use shared components from `@/components/ui` in case you need them |
| 20 | + - Component should be accessible and follow the accessibility best practices |
| 21 | + - Prefer using colors from tailwind config |
| 22 | + - For images, use a placeholder image from `@assets/images/placeholder.png` |
| 23 | + - Animated View doesn't support `className` prop, so you need to use `style` prop instead |
| 24 | + |
| 25 | +## Example |
| 26 | + |
| 27 | +Here is a example of how to write the component: |
| 28 | + |
| 29 | +```tsx |
| 30 | +import * as React from 'react'; |
| 31 | + |
| 32 | +import { Text, View, Image, SavaAreaView } from '@/components/ui'; |
| 33 | + |
| 34 | +// Props should be defined in the top of the component |
| 35 | +type TitleProps = { |
| 36 | + text: string; |
| 37 | +}; |
| 38 | + |
| 39 | +export function Title({ text }: TitleProps) { |
| 40 | + return ( |
| 41 | + <View className="flex-row items-center justify-center py-4 pb-2"> |
| 42 | + <Text className="pr-2 text-2xl">{text}</Text> |
| 43 | + <View className="h-[2px] flex-1 bg-neutral-300" /> |
| 44 | + <Image |
| 45 | + source={require('@assets/images/placeholder.png')} |
| 46 | + style={{ width: 24, height: 24 }} |
| 47 | + contentFit="contain" |
| 48 | + /> |
| 49 | + </View> |
| 50 | + ); |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +- If the screen is a form, create a form component that uses `react-hook-form` and `zod` to validate the form data and handle the form submission using the `onSubmit` prop and a console log of the form data for debugging |
| 55 | + |
| 56 | +Here is an example of how to write the form component: |
| 57 | + |
| 58 | +```tsx |
| 59 | +import { zodResolver } from '@hookform/resolvers/zod'; |
| 60 | +import React from 'react'; |
| 61 | +import type { SubmitHandler } from 'react-hook-form'; |
| 62 | +import { useForm } from 'react-hook-form'; |
| 63 | +import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; |
| 64 | +import * as z from 'zod'; |
| 65 | + |
| 66 | +import { Button, ControlledInput, Text, View } from '@/components/ui'; |
| 67 | + |
| 68 | +const schema = z.object({ |
| 69 | + name: z.string().optional(), |
| 70 | + email: z |
| 71 | + .string({ |
| 72 | + required_error: 'Email is required', |
| 73 | + }) |
| 74 | + .email('Invalid email format'), |
| 75 | + password: z |
| 76 | + .string({ |
| 77 | + required_error: 'Password is required', |
| 78 | + }) |
| 79 | + .min(6, 'Password must be at least 6 characters'), |
| 80 | +}); |
| 81 | + |
| 82 | +export type FormType = z.infer<typeof schema>; |
| 83 | + |
| 84 | +export type LoginFormProps = { |
| 85 | + onSubmit?: SubmitHandler<FormType>; |
| 86 | +}; |
| 87 | + |
| 88 | +export const LoginForm = ({ onSubmit = () => {} }: LoginFormProps) => { |
| 89 | + const { handleSubmit, control } = useForm<FormType>({ |
| 90 | + resolver: zodResolver(schema), |
| 91 | + }); |
| 92 | + return ( |
| 93 | + <KeyboardAvoidingView |
| 94 | + style={{ flex: 1 }} |
| 95 | + behavior="padding" |
| 96 | + keyboardVerticalOffset={10} |
| 97 | + > |
| 98 | + <View className="flex-1 justify-center p-4"> |
| 99 | + <View className="items-center justify-center"> |
| 100 | + <Text |
| 101 | + testID="form-title" |
| 102 | + className="pb-6 text-center text-4xl font-bold" |
| 103 | + > |
| 104 | + Sign In |
| 105 | + </Text> |
| 106 | + |
| 107 | + <Text className="mb-6 max-w-xs text-center text-gray-500"> |
| 108 | + Welcome! 👋 This is a demo login screen! Feel free to use any email |
| 109 | + and password to sign in and try it out. |
| 110 | + </Text> |
| 111 | + </View> |
| 112 | + |
| 113 | + <ControlledInput |
| 114 | + testID="name" |
| 115 | + control={control} |
| 116 | + name="name" |
| 117 | + label="Name" |
| 118 | + /> |
| 119 | + |
| 120 | + <ControlledInput |
| 121 | + testID="email-input" |
| 122 | + control={control} |
| 123 | + name="email" |
| 124 | + label="Email" |
| 125 | + /> |
| 126 | + <ControlledInput |
| 127 | + testID="password-input" |
| 128 | + control={control} |
| 129 | + name="password" |
| 130 | + label="Password" |
| 131 | + placeholder="***" |
| 132 | + secureTextEntry={true} |
| 133 | + /> |
| 134 | + <Button |
| 135 | + testID="login-button" |
| 136 | + label="Login" |
| 137 | + onPress={handleSubmit(onSubmit)} |
| 138 | + /> |
| 139 | + </View> |
| 140 | + </KeyboardAvoidingView> |
| 141 | + ); |
| 142 | +}; |
| 143 | +``` |
0 commit comments