@teardown/cli 2.0.65 → 2.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/package.json +2 -2
  2. package/src/templates/generator.ts +66 -42
  3. package/templates/{src/index.tsx → index.tsx} +5 -2
  4. package/templates/metro.config.js +7 -35
  5. package/templates/src/components/ui/accordion.tsx +23 -21
  6. package/templates/src/components/ui/card.tsx +28 -26
  7. package/templates/src/components/ui/checkbox.tsx +12 -10
  8. package/templates/src/components/ui/dialog.tsx +30 -28
  9. package/templates/src/components/ui/divider.tsx +6 -6
  10. package/templates/src/components/ui/form-field.tsx +2 -2
  11. package/templates/src/components/ui/pressable-feedback.tsx +2 -2
  12. package/templates/src/components/ui/radio-group.tsx +37 -35
  13. package/templates/src/components/ui/scroll-shadow.tsx +4 -4
  14. package/templates/src/components/ui/select.tsx +31 -29
  15. package/templates/src/components/ui/skeleton-group.tsx +2 -2
  16. package/templates/src/components/ui/skeleton.tsx +2 -2
  17. package/templates/src/components/ui/spinner.tsx +2 -2
  18. package/templates/src/components/ui/tabs.tsx +25 -23
  19. package/templates/src/global.css +86 -70
  20. package/templates/src/navigation/navigation-provider.tsx +8 -1
  21. package/templates/src/navigation/router.tsx +12 -95
  22. package/templates/src/providers/app.provider.tsx +8 -12
  23. package/templates/src/routes/_layout.tsx +2 -4
  24. package/templates/src/routes/home.tsx +112 -0
  25. package/templates/tsconfig.json +10 -12
  26. package/templates/index.ts +0 -11
  27. package/templates/src/components/ui/index.ts +0 -100
  28. package/templates/src/routes/(tabs)/_layout.tsx +0 -42
  29. package/templates/src/routes/(tabs)/explore.tsx +0 -161
  30. package/templates/src/routes/(tabs)/home.tsx +0 -138
  31. package/templates/src/routes/(tabs)/profile.tsx +0 -114
  32. package/templates/src/routes/settings.tsx +0 -184
  33. package/templates/src/screens/auth/index.ts +0 -6
  34. package/templates/src/screens/auth/login.tsx +0 -165
  35. package/templates/src/screens/auth/register.tsx +0 -203
  36. package/templates/src/screens/home.tsx +0 -204
  37. package/templates/src/screens/index.ts +0 -17
  38. package/templates/src/screens/profile.tsx +0 -210
  39. package/templates/src/screens/settings.tsx +0 -216
  40. package/templates/src/screens/welcome.tsx +0 -101
@@ -1,203 +0,0 @@
1
- /**
2
- * Register Screen
3
- *
4
- * Account registration with email/password.
5
- */
6
-
7
- import React, { useState } from "react";
8
- import { View, ScrollView, Pressable, KeyboardAvoidingView, Platform } from "react-native";
9
- import {
10
- Text,
11
- Button,
12
- Input,
13
- Card,
14
- CardHeader,
15
- CardTitle,
16
- CardDescription,
17
- CardContent,
18
- CardFooter,
19
- Separator,
20
- } from "@/components/ui";
21
-
22
- interface RegisterScreenProps {
23
- onRegister?: (name: string, email: string, password: string) => void;
24
- onLogin?: () => void;
25
- onSocialLogin?: (provider: "google" | "apple" | "github") => void;
26
- loading?: boolean;
27
- }
28
-
29
- export function RegisterScreen({
30
- onRegister,
31
- onLogin,
32
- onSocialLogin,
33
- loading = false,
34
- }: RegisterScreenProps): React.JSX.Element {
35
- const [name, setName] = useState("");
36
- const [email, setEmail] = useState("");
37
- const [password, setPassword] = useState("");
38
- const [confirmPassword, setConfirmPassword] = useState("");
39
- const [errors, setErrors] = useState<{
40
- name?: string;
41
- email?: string;
42
- password?: string;
43
- confirmPassword?: string;
44
- }>({});
45
-
46
- const validate = (): boolean => {
47
- const newErrors: typeof errors = {};
48
-
49
- if (!name.trim()) {
50
- newErrors.name = "Name is required";
51
- }
52
-
53
- if (!email) {
54
- newErrors.email = "Email is required";
55
- } else if (!/\S+@\S+\.\S+/.test(email)) {
56
- newErrors.email = "Please enter a valid email";
57
- }
58
-
59
- if (!password) {
60
- newErrors.password = "Password is required";
61
- } else if (password.length < 8) {
62
- newErrors.password = "Password must be at least 8 characters";
63
- }
64
-
65
- if (!confirmPassword) {
66
- newErrors.confirmPassword = "Please confirm your password";
67
- } else if (password !== confirmPassword) {
68
- newErrors.confirmPassword = "Passwords do not match";
69
- }
70
-
71
- setErrors(newErrors);
72
- return Object.keys(newErrors).length === 0;
73
- };
74
-
75
- const handleRegister = () => {
76
- if (validate()) {
77
- onRegister?.(name, email, password);
78
- }
79
- };
80
-
81
- return (
82
- <KeyboardAvoidingView
83
- behavior={Platform.OS === "ios" ? "padding" : "height"}
84
- className="flex-1 bg-background"
85
- >
86
- <ScrollView
87
- contentContainerClassName="flex-grow justify-center p-6"
88
- keyboardShouldPersistTaps="handled"
89
- >
90
- <Card className="w-full max-w-md self-center">
91
- <CardHeader className="items-center">
92
- <View className="mb-4 h-16 w-16 items-center justify-center rounded-2xl bg-primary">
93
- <Text className="text-2xl font-bold text-primary-foreground">T</Text>
94
- </View>
95
- <CardTitle>Create an account</CardTitle>
96
- <CardDescription>Enter your details to get started</CardDescription>
97
- </CardHeader>
98
-
99
- <CardContent className="gap-4">
100
- {/* Social Login Buttons */}
101
- <View className="gap-3">
102
- <Button
103
- variant="outline"
104
- fullWidth
105
- onPress={() => onSocialLogin?.("google")}
106
- >
107
- <View className="flex-row items-center gap-2">
108
- <Text>🔵</Text>
109
- <Text className="font-semibold text-foreground">Continue with Google</Text>
110
- </View>
111
- </Button>
112
-
113
- <Button
114
- variant="outline"
115
- fullWidth
116
- onPress={() => onSocialLogin?.("apple")}
117
- >
118
- <View className="flex-row items-center gap-2">
119
- <Text></Text>
120
- <Text className="font-semibold text-foreground">Continue with Apple</Text>
121
- </View>
122
- </Button>
123
- </View>
124
-
125
- {/* Divider */}
126
- <View className="flex-row items-center gap-4 my-2">
127
- <Separator className="flex-1" />
128
- <Text variant="muted">or</Text>
129
- <Separator className="flex-1" />
130
- </View>
131
-
132
- {/* Registration Form */}
133
- <View className="gap-4">
134
- <Input
135
- label="Full Name"
136
- placeholder="John Doe"
137
- value={name}
138
- onChangeText={setName}
139
- error={errors.name}
140
- autoCapitalize="words"
141
- autoComplete="name"
142
- />
143
-
144
- <Input
145
- label="Email"
146
- placeholder="you@example.com"
147
- value={email}
148
- onChangeText={setEmail}
149
- error={errors.email}
150
- keyboardType="email-address"
151
- autoCapitalize="none"
152
- autoComplete="email"
153
- />
154
-
155
- <Input
156
- label="Password"
157
- placeholder="Create a password"
158
- value={password}
159
- onChangeText={setPassword}
160
- error={errors.password}
161
- hint="Must be at least 8 characters"
162
- secureTextEntry
163
- autoComplete="new-password"
164
- />
165
-
166
- <Input
167
- label="Confirm Password"
168
- placeholder="Confirm your password"
169
- value={confirmPassword}
170
- onChangeText={setConfirmPassword}
171
- error={errors.confirmPassword}
172
- secureTextEntry
173
- autoComplete="new-password"
174
- />
175
- </View>
176
-
177
- {/* Terms */}
178
- <Text variant="muted" className="text-center">
179
- By creating an account, you agree to our{" "}
180
- <Text className="text-primary">Terms of Service</Text> and{" "}
181
- <Text className="text-primary">Privacy Policy</Text>
182
- </Text>
183
- </CardContent>
184
-
185
- <CardFooter className="flex-col gap-4">
186
- <Button fullWidth loading={loading} onPress={handleRegister}>
187
- Create Account
188
- </Button>
189
-
190
- <View className="flex-row items-center justify-center gap-1">
191
- <Text variant="muted">Already have an account?</Text>
192
- <Pressable onPress={onLogin}>
193
- <Text className="font-semibold text-primary">Sign in</Text>
194
- </Pressable>
195
- </View>
196
- </CardFooter>
197
- </Card>
198
- </ScrollView>
199
- </KeyboardAvoidingView>
200
- );
201
- }
202
-
203
- export default RegisterScreen;
@@ -1,204 +0,0 @@
1
- /**
2
- * Home Screen
3
- *
4
- * Main dashboard/home screen with content feed.
5
- */
6
-
7
- import React from "react";
8
- import { View, ScrollView, Pressable, RefreshControl } from "react-native";
9
- import {
10
- Text,
11
- Avatar,
12
- Card,
13
- CardHeader,
14
- CardContent,
15
- CardFooter,
16
- Badge,
17
- Skeleton,
18
- SkeletonCircle,
19
- } from "@/components/ui";
20
-
21
- interface Post {
22
- id: string;
23
- author: {
24
- name: string;
25
- avatar?: string;
26
- isVerified?: boolean;
27
- };
28
- content: string;
29
- timestamp: string;
30
- likes: number;
31
- comments: number;
32
- isLiked?: boolean;
33
- }
34
-
35
- interface HomeScreenProps {
36
- posts?: Post[];
37
- loading?: boolean;
38
- refreshing?: boolean;
39
- onRefresh?: () => void;
40
- onPostPress?: (post: Post) => void;
41
- onProfilePress?: () => void;
42
- onNotificationsPress?: () => void;
43
- }
44
-
45
- const mockPosts: Post[] = [
46
- {
47
- id: "1",
48
- author: { name: "Jane Smith", isVerified: true },
49
- content:
50
- "Just shipped a new feature using Uniwind! The Tailwind CSS integration for React Native is a game-changer. Build UIs so much faster now!",
51
- timestamp: "2h ago",
52
- likes: 42,
53
- comments: 8,
54
- isLiked: true,
55
- },
56
- {
57
- id: "2",
58
- author: { name: "Alex Johnson" },
59
- content:
60
- "Working on something exciting with Teardown CLI. The developer experience is incredible - fast builds, great templates, and now with Uniwind support!",
61
- timestamp: "4h ago",
62
- likes: 28,
63
- comments: 5,
64
- },
65
- {
66
- id: "3",
67
- author: { name: "Sam Wilson", isVerified: true },
68
- content:
69
- "Pro tip: Use the prebuilt components from the template as a starting point. They're fully customizable and follow best practices.",
70
- timestamp: "6h ago",
71
- likes: 156,
72
- comments: 23,
73
- },
74
- ];
75
-
76
- export function HomeScreen({
77
- posts = mockPosts,
78
- loading = false,
79
- refreshing = false,
80
- onRefresh,
81
- onPostPress,
82
- onProfilePress,
83
- onNotificationsPress,
84
- }: HomeScreenProps): React.JSX.Element {
85
- return (
86
- <View className="flex-1 bg-background">
87
- {/* Header */}
88
- <View className="flex-row items-center justify-between px-6 py-4 border-b border-border">
89
- <Pressable onPress={onProfilePress}>
90
- <Avatar fallback="U" size="sm" />
91
- </Pressable>
92
- <Text variant="h4">Home</Text>
93
- <Pressable onPress={onNotificationsPress} className="relative">
94
- <Text className="text-xl">🔔</Text>
95
- <View className="absolute -top-1 -right-1 h-3 w-3 rounded-full bg-destructive" />
96
- </Pressable>
97
- </View>
98
-
99
- {/* Content */}
100
- <ScrollView
101
- className="flex-1"
102
- contentContainerClassName="p-4 gap-4"
103
- refreshControl={
104
- <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
105
- }
106
- >
107
- {loading ? (
108
- // Loading skeleton
109
- <>
110
- <PostSkeleton />
111
- <PostSkeleton />
112
- <PostSkeleton />
113
- </>
114
- ) : (
115
- // Posts
116
- posts.map((post) => (
117
- <PostCard
118
- key={post.id}
119
- post={post}
120
- onPress={() => onPostPress?.(post)}
121
- />
122
- ))
123
- )}
124
- </ScrollView>
125
- </View>
126
- );
127
- }
128
-
129
- function PostCard({
130
- post,
131
- onPress,
132
- }: {
133
- post: Post;
134
- onPress?: () => void;
135
- }): React.JSX.Element {
136
- return (
137
- <Pressable onPress={onPress}>
138
- <Card>
139
- <CardHeader className="flex-row items-center gap-3 pb-2">
140
- <Avatar fallback={post.author.name} size="md" />
141
- <View className="flex-1">
142
- <View className="flex-row items-center gap-2">
143
- <Text variant="large">{post.author.name}</Text>
144
- {post.author.isVerified && (
145
- <Badge variant="default" className="h-5">
146
- <Text className="text-xs text-primary-foreground">✓</Text>
147
- </Badge>
148
- )}
149
- </View>
150
- <Text variant="muted">{post.timestamp}</Text>
151
- </View>
152
- </CardHeader>
153
-
154
- <CardContent>
155
- <Text>{post.content}</Text>
156
- </CardContent>
157
-
158
- <CardFooter className="gap-6 pt-2">
159
- <Pressable className="flex-row items-center gap-2">
160
- <Text className={post.isLiked ? "text-destructive" : ""}>
161
- {post.isLiked ? "❤️" : "🤍"}
162
- </Text>
163
- <Text variant="muted">{post.likes}</Text>
164
- </Pressable>
165
-
166
- <Pressable className="flex-row items-center gap-2">
167
- <Text>💬</Text>
168
- <Text variant="muted">{post.comments}</Text>
169
- </Pressable>
170
-
171
- <Pressable className="flex-row items-center gap-2">
172
- <Text>📤</Text>
173
- </Pressable>
174
- </CardFooter>
175
- </Card>
176
- </Pressable>
177
- );
178
- }
179
-
180
- function PostSkeleton(): React.JSX.Element {
181
- return (
182
- <Card>
183
- <CardHeader className="flex-row items-center gap-3 pb-2">
184
- <SkeletonCircle size={40} />
185
- <View className="flex-1 gap-2">
186
- <Skeleton className="h-4 w-32" />
187
- <Skeleton className="h-3 w-20" />
188
- </View>
189
- </CardHeader>
190
- <CardContent className="gap-2">
191
- <Skeleton className="h-4 w-full" />
192
- <Skeleton className="h-4 w-full" />
193
- <Skeleton className="h-4 w-3/4" />
194
- </CardContent>
195
- <CardFooter className="gap-6 pt-2">
196
- <Skeleton className="h-4 w-12" />
197
- <Skeleton className="h-4 w-12" />
198
- <Skeleton className="h-4 w-8" />
199
- </CardFooter>
200
- </Card>
201
- );
202
- }
203
-
204
- export default HomeScreen;
@@ -1,17 +0,0 @@
1
- /**
2
- * Screens Barrel Export
3
- *
4
- * Pre-built screens using Uniwind components.
5
- * Use these as starting points for your app.
6
- */
7
-
8
- // Onboarding
9
- export { WelcomeScreen } from "./welcome";
10
-
11
- // Authentication
12
- export { LoginScreen, RegisterScreen } from "./auth";
13
-
14
- // Main App
15
- export { HomeScreen } from "./home";
16
- export { ProfileScreen } from "./profile";
17
- export { SettingsScreen } from "./settings";
@@ -1,210 +0,0 @@
1
- /**
2
- * Profile Screen
3
- *
4
- * User profile display with edit capabilities.
5
- */
6
-
7
- import React from "react";
8
- import { View, ScrollView, Pressable } from "react-native";
9
- import {
10
- Text,
11
- Button,
12
- Avatar,
13
- Card,
14
- CardContent,
15
- Badge,
16
- Separator,
17
- } from "@/components/ui";
18
-
19
- interface ProfileScreenProps {
20
- user?: {
21
- name: string;
22
- email: string;
23
- avatar?: string;
24
- bio?: string;
25
- location?: string;
26
- joinedDate?: string;
27
- isPro?: boolean;
28
- };
29
- stats?: {
30
- posts: number;
31
- followers: number;
32
- following: number;
33
- };
34
- onEditProfile?: () => void;
35
- onSettings?: () => void;
36
- onLogout?: () => void;
37
- }
38
-
39
- export function ProfileScreen({
40
- user = {
41
- name: "John Doe",
42
- email: "john@example.com",
43
- bio: "Building amazing apps with React Native and Tailwind CSS",
44
- location: "San Francisco, CA",
45
- joinedDate: "January 2024",
46
- isPro: true,
47
- },
48
- stats = {
49
- posts: 42,
50
- followers: 1234,
51
- following: 567,
52
- },
53
- onEditProfile,
54
- onSettings,
55
- onLogout,
56
- }: ProfileScreenProps): React.JSX.Element {
57
- return (
58
- <ScrollView className="flex-1 bg-background">
59
- {/* Header */}
60
- <View className="bg-primary/5 pb-6 pt-4">
61
- <View className="flex-row items-center justify-between px-6">
62
- <Text variant="h3">Profile</Text>
63
- <Pressable onPress={onSettings} className="p-2">
64
- <Text className="text-xl">⚙️</Text>
65
- </Pressable>
66
- </View>
67
-
68
- {/* Profile Info */}
69
- <View className="items-center mt-6 px-6">
70
- <Avatar
71
- fallback={user.name}
72
- size="xl"
73
- className="border-4 border-background"
74
- />
75
- <View className="items-center mt-4 gap-1">
76
- <View className="flex-row items-center gap-2">
77
- <Text variant="h3">{user.name}</Text>
78
- {user.isPro && <Badge variant="default">PRO</Badge>}
79
- </View>
80
- <Text variant="muted">{user.email}</Text>
81
- </View>
82
- </View>
83
-
84
- {/* Stats */}
85
- <View className="flex-row justify-center gap-8 mt-6">
86
- <StatItem label="Posts" value={stats.posts} />
87
- <StatItem label="Followers" value={stats.followers} />
88
- <StatItem label="Following" value={stats.following} />
89
- </View>
90
-
91
- {/* Actions */}
92
- <View className="flex-row gap-3 mt-6 px-6">
93
- <Button variant="outline" className="flex-1" onPress={onEditProfile}>
94
- Edit Profile
95
- </Button>
96
- <Button variant="outline" size="icon">
97
- <Text>📤</Text>
98
- </Button>
99
- </View>
100
- </View>
101
-
102
- {/* Content */}
103
- <View className="p-6 gap-4">
104
- {/* Bio Card */}
105
- <Card>
106
- <CardContent className="gap-4 py-4">
107
- <View className="flex-row items-center gap-2">
108
- <Text>📝</Text>
109
- <Text variant="label">About</Text>
110
- </View>
111
- <Text>{user.bio}</Text>
112
- </CardContent>
113
- </Card>
114
-
115
- {/* Details Card */}
116
- <Card>
117
- <CardContent className="gap-4 py-4">
118
- <View className="flex-row items-center gap-2">
119
- <Text>ℹ️</Text>
120
- <Text variant="label">Details</Text>
121
- </View>
122
-
123
- <View className="gap-3">
124
- <DetailRow icon="📍" label="Location" value={user.location || "Not set"} />
125
- <Separator />
126
- <DetailRow icon="📅" label="Joined" value={user.joinedDate || "Unknown"} />
127
- </View>
128
- </CardContent>
129
- </Card>
130
-
131
- {/* Quick Actions */}
132
- <Card>
133
- <CardContent className="gap-1 py-2">
134
- <ActionRow icon="🔔" label="Notifications" onPress={() => {}} />
135
- <Separator />
136
- <ActionRow icon="🔒" label="Privacy & Security" onPress={() => {}} />
137
- <Separator />
138
- <ActionRow icon="❓" label="Help & Support" onPress={() => {}} />
139
- </CardContent>
140
- </Card>
141
-
142
- {/* Logout */}
143
- <Button variant="destructive" fullWidth onPress={onLogout}>
144
- Sign Out
145
- </Button>
146
- </View>
147
- </ScrollView>
148
- );
149
- }
150
-
151
- function StatItem({ label, value }: { label: string; value: number }): React.JSX.Element {
152
- const formatNumber = (num: number): string => {
153
- if (num >= 1000) {
154
- return `${(num / 1000).toFixed(1)}k`;
155
- }
156
- return num.toString();
157
- };
158
-
159
- return (
160
- <View className="items-center">
161
- <Text variant="h3">{formatNumber(value)}</Text>
162
- <Text variant="muted">{label}</Text>
163
- </View>
164
- );
165
- }
166
-
167
- function DetailRow({
168
- icon,
169
- label,
170
- value,
171
- }: {
172
- icon: string;
173
- label: string;
174
- value: string;
175
- }): React.JSX.Element {
176
- return (
177
- <View className="flex-row items-center justify-between">
178
- <View className="flex-row items-center gap-2">
179
- <Text>{icon}</Text>
180
- <Text variant="muted">{label}</Text>
181
- </View>
182
- <Text>{value}</Text>
183
- </View>
184
- );
185
- }
186
-
187
- function ActionRow({
188
- icon,
189
- label,
190
- onPress,
191
- }: {
192
- icon: string;
193
- label: string;
194
- onPress: () => void;
195
- }): React.JSX.Element {
196
- return (
197
- <Pressable
198
- className="flex-row items-center justify-between py-3 active:bg-muted rounded-lg px-2 -mx-2"
199
- onPress={onPress}
200
- >
201
- <View className="flex-row items-center gap-3">
202
- <Text className="text-lg">{icon}</Text>
203
- <Text>{label}</Text>
204
- </View>
205
- <Text className="text-muted-foreground">›</Text>
206
- </Pressable>
207
- );
208
- }
209
-
210
- export default ProfileScreen;