create-100x-mobile 0.4.3 → 0.4.5
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.
- package/dist/cli.js +3 -1
- package/dist/commands/new/args.js +18 -0
- package/dist/commands/new/scaffold.js +6 -6
- package/dist/commands/new/steps.js +99 -19
- package/dist/commands/new.js +108 -38
- package/dist/templates/app/authLayout.js +0 -1
- package/dist/templates/app/signIn.js +1 -15
- package/dist/templates/config/envExample.js +6 -1
- package/dist/templates/config/packageJson.js +13 -3
- package/dist/templates/config/readme.js +45 -1
- package/dist/templates/instant/instantLib.js +28 -0
- package/dist/templates/instant/rootLayout.js +23 -0
- package/dist/templates/instant/settingsScreen.js +83 -0
- package/dist/templates/instant/tabsLayout.js +54 -0
- package/dist/templates/instant/todosScreen.js +408 -0
- package/package.json +1 -1
- package/dist/templates/app/signUp.js +0 -298
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instantLibTemplate = instantLibTemplate;
|
|
4
|
+
function instantLibTemplate() {
|
|
5
|
+
return `import "react-native-get-random-values";
|
|
6
|
+
import { init, i, id, type InstaQLEntity } from "@instantdb/react-native";
|
|
7
|
+
|
|
8
|
+
const appId = process.env.EXPO_PUBLIC_INSTANT_APP_ID;
|
|
9
|
+
|
|
10
|
+
export const schema = i.schema({
|
|
11
|
+
entities: {
|
|
12
|
+
todos: i.entity({
|
|
13
|
+
text: i.string(),
|
|
14
|
+
completed: i.boolean(),
|
|
15
|
+
createdAt: i.number().indexed(),
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type InstantTodo = InstaQLEntity<typeof schema, "todos">;
|
|
21
|
+
export const createId = id;
|
|
22
|
+
|
|
23
|
+
export const isInstantConfigured = Boolean(appId);
|
|
24
|
+
export const instantDb = isInstantConfigured
|
|
25
|
+
? init({ appId: appId as string, schema })
|
|
26
|
+
: null;
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instantRootLayoutTemplate = instantRootLayoutTemplate;
|
|
4
|
+
function instantRootLayoutTemplate() {
|
|
5
|
+
return `import { Stack } from "expo-router";
|
|
6
|
+
import { StatusBar } from "expo-status-bar";
|
|
7
|
+
import { useFrameworkReady } from "@/hooks/useFrameworkReady";
|
|
8
|
+
|
|
9
|
+
export default function RootLayout() {
|
|
10
|
+
useFrameworkReady();
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<Stack screenOptions={{ headerShown: false }}>
|
|
15
|
+
<Stack.Screen name="(tabs)" />
|
|
16
|
+
<Stack.Screen name="+not-found" />
|
|
17
|
+
</Stack>
|
|
18
|
+
<StatusBar style="auto" />
|
|
19
|
+
</>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instantSettingsScreenTemplate = instantSettingsScreenTemplate;
|
|
4
|
+
function instantSettingsScreenTemplate() {
|
|
5
|
+
return `import React from "react";
|
|
6
|
+
import { View, Text, StyleSheet, StatusBar } from "react-native";
|
|
7
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
8
|
+
|
|
9
|
+
export default function SettingsScreen() {
|
|
10
|
+
const instantAppId = process.env.EXPO_PUBLIC_INSTANT_APP_ID;
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<SafeAreaView style={styles.container}>
|
|
14
|
+
<StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
|
|
15
|
+
<View style={styles.header}>
|
|
16
|
+
<Text style={styles.title}>Settings</Text>
|
|
17
|
+
</View>
|
|
18
|
+
|
|
19
|
+
<View style={styles.content}>
|
|
20
|
+
<View style={styles.card}>
|
|
21
|
+
<Text style={styles.sectionTitle}>Backend</Text>
|
|
22
|
+
<Text style={styles.sectionText}>InstantDB</Text>
|
|
23
|
+
</View>
|
|
24
|
+
|
|
25
|
+
<View style={styles.card}>
|
|
26
|
+
<Text style={styles.sectionTitle}>Instant App ID</Text>
|
|
27
|
+
<Text style={styles.sectionText}>
|
|
28
|
+
{instantAppId || "Not configured (set EXPO_PUBLIC_INSTANT_APP_ID)"}
|
|
29
|
+
</Text>
|
|
30
|
+
</View>
|
|
31
|
+
|
|
32
|
+
<View style={styles.card}>
|
|
33
|
+
<Text style={styles.sectionTitle}>About</Text>
|
|
34
|
+
<Text style={styles.sectionText}>Version 1.0.0</Text>
|
|
35
|
+
<Text style={styles.sectionText}>Built with Expo + InstantDB</Text>
|
|
36
|
+
</View>
|
|
37
|
+
</View>
|
|
38
|
+
</SafeAreaView>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const styles = StyleSheet.create({
|
|
43
|
+
container: {
|
|
44
|
+
flex: 1,
|
|
45
|
+
backgroundColor: "#FFFFFF",
|
|
46
|
+
},
|
|
47
|
+
header: {
|
|
48
|
+
paddingHorizontal: 16,
|
|
49
|
+
paddingTop: 24,
|
|
50
|
+
paddingBottom: 16,
|
|
51
|
+
},
|
|
52
|
+
title: {
|
|
53
|
+
fontSize: 28,
|
|
54
|
+
fontWeight: "700",
|
|
55
|
+
color: "#1F2937",
|
|
56
|
+
},
|
|
57
|
+
content: {
|
|
58
|
+
paddingHorizontal: 16,
|
|
59
|
+
paddingBottom: 24,
|
|
60
|
+
gap: 12,
|
|
61
|
+
},
|
|
62
|
+
card: {
|
|
63
|
+
backgroundColor: "#F8F9FA",
|
|
64
|
+
borderRadius: 14,
|
|
65
|
+
padding: 16,
|
|
66
|
+
},
|
|
67
|
+
sectionTitle: {
|
|
68
|
+
fontSize: 14,
|
|
69
|
+
fontWeight: "600",
|
|
70
|
+
color: "#374151",
|
|
71
|
+
marginBottom: 8,
|
|
72
|
+
textTransform: "uppercase",
|
|
73
|
+
letterSpacing: 0.4,
|
|
74
|
+
},
|
|
75
|
+
sectionText: {
|
|
76
|
+
fontSize: 15,
|
|
77
|
+
color: "#1F2937",
|
|
78
|
+
marginBottom: 4,
|
|
79
|
+
lineHeight: 22,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instantTabsLayoutTemplate = instantTabsLayoutTemplate;
|
|
4
|
+
function instantTabsLayoutTemplate() {
|
|
5
|
+
return `import { Tabs } from "expo-router";
|
|
6
|
+
import { SquareCheck as CheckSquare, Settings } from "lucide-react-native";
|
|
7
|
+
import { Platform } from "react-native";
|
|
8
|
+
|
|
9
|
+
export default function TabLayout() {
|
|
10
|
+
return (
|
|
11
|
+
<Tabs
|
|
12
|
+
screenOptions={{
|
|
13
|
+
headerShown: false,
|
|
14
|
+
tabBarStyle: {
|
|
15
|
+
backgroundColor: "#FFFFFF",
|
|
16
|
+
borderTopWidth: 0,
|
|
17
|
+
elevation: 0,
|
|
18
|
+
shadowOpacity: 0,
|
|
19
|
+
height: Platform.OS === "ios" ? 88 : 72,
|
|
20
|
+
paddingBottom: Platform.OS === "ios" ? 32 : 12,
|
|
21
|
+
paddingTop: 12,
|
|
22
|
+
},
|
|
23
|
+
tabBarActiveTintColor: "#1F2937",
|
|
24
|
+
tabBarInactiveTintColor: "#9CA3AF",
|
|
25
|
+
tabBarLabelStyle: {
|
|
26
|
+
fontSize: 13,
|
|
27
|
+
fontWeight: "500",
|
|
28
|
+
marginTop: 4,
|
|
29
|
+
},
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<Tabs.Screen
|
|
33
|
+
name="index"
|
|
34
|
+
options={{
|
|
35
|
+
title: "Todos",
|
|
36
|
+
tabBarIcon: ({ size, color }) => (
|
|
37
|
+
<CheckSquare size={size} color={color} strokeWidth={2} />
|
|
38
|
+
),
|
|
39
|
+
}}
|
|
40
|
+
/>
|
|
41
|
+
<Tabs.Screen
|
|
42
|
+
name="settings"
|
|
43
|
+
options={{
|
|
44
|
+
title: "Settings",
|
|
45
|
+
tabBarIcon: ({ size, color }) => (
|
|
46
|
+
<Settings size={size} color={color} strokeWidth={2} />
|
|
47
|
+
),
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
</Tabs>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instantTodosScreenTemplate = instantTodosScreenTemplate;
|
|
4
|
+
function instantTodosScreenTemplate() {
|
|
5
|
+
return `import React, { useMemo, useState } from "react";
|
|
6
|
+
import {
|
|
7
|
+
Alert,
|
|
8
|
+
FlatList,
|
|
9
|
+
KeyboardAvoidingView,
|
|
10
|
+
Platform,
|
|
11
|
+
Pressable,
|
|
12
|
+
StatusBar,
|
|
13
|
+
StyleSheet,
|
|
14
|
+
Text,
|
|
15
|
+
TextInput,
|
|
16
|
+
View,
|
|
17
|
+
} from "react-native";
|
|
18
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
19
|
+
import { Check, Plus, Trash2 } from "lucide-react-native";
|
|
20
|
+
import { createId, instantDb, type InstantTodo } from "@/lib/instant";
|
|
21
|
+
|
|
22
|
+
type FilterType = "all" | "active" | "completed";
|
|
23
|
+
|
|
24
|
+
function SetupRequired() {
|
|
25
|
+
return (
|
|
26
|
+
<View style={styles.setupContainer}>
|
|
27
|
+
<Text style={styles.setupTitle}>InstantDB Setup Required</Text>
|
|
28
|
+
<Text style={styles.setupText}>
|
|
29
|
+
Add EXPO_PUBLIC_INSTANT_APP_ID to your .env.local file.
|
|
30
|
+
</Text>
|
|
31
|
+
<Text style={styles.setupHint}>
|
|
32
|
+
You can create an app id with:\\n
|
|
33
|
+
npx instant-cli init-without-files --title my-app
|
|
34
|
+
</Text>
|
|
35
|
+
</View>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function InstantTodosScreen() {
|
|
40
|
+
const db = instantDb!;
|
|
41
|
+
const [text, setText] = useState("");
|
|
42
|
+
const [filter, setFilter] = useState<FilterType>("all");
|
|
43
|
+
|
|
44
|
+
const { isLoading, error, data } = db.useQuery({
|
|
45
|
+
todos: {
|
|
46
|
+
$: {
|
|
47
|
+
order: { createdAt: "desc" },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const todos = data?.todos ?? [];
|
|
53
|
+
|
|
54
|
+
const filteredTodos = useMemo(() => {
|
|
55
|
+
switch (filter) {
|
|
56
|
+
case "active":
|
|
57
|
+
return todos.filter((todo) => !todo.completed);
|
|
58
|
+
case "completed":
|
|
59
|
+
return todos.filter((todo) => todo.completed);
|
|
60
|
+
default:
|
|
61
|
+
return todos;
|
|
62
|
+
}
|
|
63
|
+
}, [filter, todos]);
|
|
64
|
+
|
|
65
|
+
const activeTodosCount = todos.filter((todo) => !todo.completed).length;
|
|
66
|
+
const completedTodosCount = todos.filter((todo) => todo.completed).length;
|
|
67
|
+
|
|
68
|
+
const addTodo = async () => {
|
|
69
|
+
const trimmed = text.trim();
|
|
70
|
+
if (!trimmed) return;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
await db.transact(
|
|
74
|
+
db.tx.todos[createId()].create({
|
|
75
|
+
text: trimmed,
|
|
76
|
+
completed: false,
|
|
77
|
+
createdAt: Date.now(),
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
setText("");
|
|
81
|
+
} catch (txnError) {
|
|
82
|
+
console.error("Failed to add todo:", txnError);
|
|
83
|
+
Alert.alert("Error", "Could not add todo.");
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const toggleTodo = async (todo: InstantTodo) => {
|
|
88
|
+
try {
|
|
89
|
+
await db.transact(
|
|
90
|
+
db.tx.todos[todo.id].update({ completed: !todo.completed })
|
|
91
|
+
);
|
|
92
|
+
} catch (txnError) {
|
|
93
|
+
console.error("Failed to toggle todo:", txnError);
|
|
94
|
+
Alert.alert("Error", "Could not update todo.");
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const removeTodo = async (todo: InstantTodo) => {
|
|
99
|
+
try {
|
|
100
|
+
await db.transact(db.tx.todos[todo.id].delete());
|
|
101
|
+
} catch (txnError) {
|
|
102
|
+
console.error("Failed to delete todo:", txnError);
|
|
103
|
+
Alert.alert("Error", "Could not delete todo.");
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (isLoading) {
|
|
108
|
+
return (
|
|
109
|
+
<View style={styles.loadingContainer}>
|
|
110
|
+
<Text style={styles.loadingText}>Loading todos...</Text>
|
|
111
|
+
</View>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (error) {
|
|
116
|
+
return (
|
|
117
|
+
<View style={styles.loadingContainer}>
|
|
118
|
+
<Text style={styles.errorText}>Error: {error.message}</Text>
|
|
119
|
+
</View>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<SafeAreaView style={styles.container}>
|
|
125
|
+
<StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
|
|
126
|
+
<KeyboardAvoidingView
|
|
127
|
+
style={styles.keyboardAvoidingView}
|
|
128
|
+
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
129
|
+
>
|
|
130
|
+
<View style={styles.header}>
|
|
131
|
+
<Text style={styles.title}>My Tasks</Text>
|
|
132
|
+
<Text style={styles.subtitle}>
|
|
133
|
+
{activeTodosCount} active, {completedTodosCount} completed
|
|
134
|
+
</Text>
|
|
135
|
+
</View>
|
|
136
|
+
|
|
137
|
+
<View style={styles.addRow}>
|
|
138
|
+
<TextInput
|
|
139
|
+
style={styles.input}
|
|
140
|
+
placeholder="Add a new task..."
|
|
141
|
+
placeholderTextColor="#9CA3AF"
|
|
142
|
+
value={text}
|
|
143
|
+
onChangeText={setText}
|
|
144
|
+
onSubmitEditing={addTodo}
|
|
145
|
+
returnKeyType="done"
|
|
146
|
+
/>
|
|
147
|
+
<Pressable
|
|
148
|
+
style={[styles.addButton, !text.trim() && styles.addButtonDisabled]}
|
|
149
|
+
onPress={addTodo}
|
|
150
|
+
disabled={!text.trim()}
|
|
151
|
+
>
|
|
152
|
+
<Plus size={18} color={text.trim() ? "#FFFFFF" : "#9CA3AF"} />
|
|
153
|
+
</Pressable>
|
|
154
|
+
</View>
|
|
155
|
+
|
|
156
|
+
<View style={styles.filters}>
|
|
157
|
+
{(["all", "active", "completed"] as const).map((key) => (
|
|
158
|
+
<Pressable
|
|
159
|
+
key={key}
|
|
160
|
+
style={[styles.filterPill, filter === key && styles.filterPillActive]}
|
|
161
|
+
onPress={() => setFilter(key)}
|
|
162
|
+
>
|
|
163
|
+
<Text
|
|
164
|
+
style={[
|
|
165
|
+
styles.filterText,
|
|
166
|
+
filter === key && styles.filterTextActive,
|
|
167
|
+
]}
|
|
168
|
+
>
|
|
169
|
+
{key === "completed" ? "done" : key}
|
|
170
|
+
</Text>
|
|
171
|
+
</Pressable>
|
|
172
|
+
))}
|
|
173
|
+
</View>
|
|
174
|
+
|
|
175
|
+
<FlatList
|
|
176
|
+
data={filteredTodos}
|
|
177
|
+
keyExtractor={(item) => item.id}
|
|
178
|
+
contentContainerStyle={styles.listContent}
|
|
179
|
+
ListEmptyComponent={
|
|
180
|
+
<View style={styles.emptyState}>
|
|
181
|
+
<Text style={styles.emptyTitle}>No tasks yet</Text>
|
|
182
|
+
<Text style={styles.emptySubtitle}>
|
|
183
|
+
Add your first task above to get started.
|
|
184
|
+
</Text>
|
|
185
|
+
</View>
|
|
186
|
+
}
|
|
187
|
+
renderItem={({ item }) => (
|
|
188
|
+
<View style={styles.todoRow}>
|
|
189
|
+
<Pressable
|
|
190
|
+
style={[
|
|
191
|
+
styles.checkbox,
|
|
192
|
+
item.completed && styles.checkboxCompleted,
|
|
193
|
+
]}
|
|
194
|
+
onPress={() => toggleTodo(item)}
|
|
195
|
+
>
|
|
196
|
+
{item.completed ? (
|
|
197
|
+
<Check size={14} color="#FFFFFF" strokeWidth={3} />
|
|
198
|
+
) : null}
|
|
199
|
+
</Pressable>
|
|
200
|
+
<Text
|
|
201
|
+
style={[
|
|
202
|
+
styles.todoText,
|
|
203
|
+
item.completed && styles.todoTextCompleted,
|
|
204
|
+
]}
|
|
205
|
+
>
|
|
206
|
+
{item.text}
|
|
207
|
+
</Text>
|
|
208
|
+
<Pressable style={styles.deleteButton} onPress={() => removeTodo(item)}>
|
|
209
|
+
<Trash2 size={18} color="#9CA3AF" />
|
|
210
|
+
</Pressable>
|
|
211
|
+
</View>
|
|
212
|
+
)}
|
|
213
|
+
/>
|
|
214
|
+
</KeyboardAvoidingView>
|
|
215
|
+
</SafeAreaView>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export default function TodosScreen() {
|
|
220
|
+
if (!instantDb) {
|
|
221
|
+
return (
|
|
222
|
+
<SafeAreaView style={styles.container}>
|
|
223
|
+
<SetupRequired />
|
|
224
|
+
</SafeAreaView>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return <InstantTodosScreen />;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const styles = StyleSheet.create({
|
|
231
|
+
container: {
|
|
232
|
+
flex: 1,
|
|
233
|
+
backgroundColor: "#FFFFFF",
|
|
234
|
+
},
|
|
235
|
+
keyboardAvoidingView: {
|
|
236
|
+
flex: 1,
|
|
237
|
+
},
|
|
238
|
+
header: {
|
|
239
|
+
paddingHorizontal: 16,
|
|
240
|
+
paddingTop: 24,
|
|
241
|
+
paddingBottom: 16,
|
|
242
|
+
},
|
|
243
|
+
title: {
|
|
244
|
+
fontSize: 28,
|
|
245
|
+
fontWeight: "700",
|
|
246
|
+
color: "#1F2937",
|
|
247
|
+
marginBottom: 4,
|
|
248
|
+
},
|
|
249
|
+
subtitle: {
|
|
250
|
+
fontSize: 14,
|
|
251
|
+
color: "#6B7280",
|
|
252
|
+
fontWeight: "500",
|
|
253
|
+
},
|
|
254
|
+
addRow: {
|
|
255
|
+
paddingHorizontal: 16,
|
|
256
|
+
flexDirection: "row",
|
|
257
|
+
alignItems: "center",
|
|
258
|
+
gap: 8,
|
|
259
|
+
},
|
|
260
|
+
input: {
|
|
261
|
+
flex: 1,
|
|
262
|
+
height: 52,
|
|
263
|
+
borderWidth: 1,
|
|
264
|
+
borderColor: "#E5E7EB",
|
|
265
|
+
borderRadius: 12,
|
|
266
|
+
paddingHorizontal: 14,
|
|
267
|
+
fontSize: 16,
|
|
268
|
+
color: "#1F2937",
|
|
269
|
+
},
|
|
270
|
+
addButton: {
|
|
271
|
+
width: 44,
|
|
272
|
+
height: 44,
|
|
273
|
+
borderRadius: 12,
|
|
274
|
+
backgroundColor: "#1F2937",
|
|
275
|
+
alignItems: "center",
|
|
276
|
+
justifyContent: "center",
|
|
277
|
+
},
|
|
278
|
+
addButtonDisabled: {
|
|
279
|
+
backgroundColor: "#E5E7EB",
|
|
280
|
+
},
|
|
281
|
+
filters: {
|
|
282
|
+
flexDirection: "row",
|
|
283
|
+
paddingHorizontal: 16,
|
|
284
|
+
paddingTop: 14,
|
|
285
|
+
paddingBottom: 10,
|
|
286
|
+
gap: 8,
|
|
287
|
+
},
|
|
288
|
+
filterPill: {
|
|
289
|
+
paddingHorizontal: 12,
|
|
290
|
+
paddingVertical: 8,
|
|
291
|
+
borderRadius: 999,
|
|
292
|
+
backgroundColor: "#F3F4F6",
|
|
293
|
+
},
|
|
294
|
+
filterPillActive: {
|
|
295
|
+
backgroundColor: "#111827",
|
|
296
|
+
},
|
|
297
|
+
filterText: {
|
|
298
|
+
color: "#6B7280",
|
|
299
|
+
fontWeight: "600",
|
|
300
|
+
textTransform: "capitalize",
|
|
301
|
+
},
|
|
302
|
+
filterTextActive: {
|
|
303
|
+
color: "#FFFFFF",
|
|
304
|
+
},
|
|
305
|
+
listContent: {
|
|
306
|
+
paddingHorizontal: 16,
|
|
307
|
+
paddingBottom: 96,
|
|
308
|
+
flexGrow: 1,
|
|
309
|
+
},
|
|
310
|
+
todoRow: {
|
|
311
|
+
flexDirection: "row",
|
|
312
|
+
alignItems: "center",
|
|
313
|
+
minHeight: 56,
|
|
314
|
+
borderRadius: 12,
|
|
315
|
+
borderWidth: 1,
|
|
316
|
+
borderColor: "#E5E7EB",
|
|
317
|
+
marginBottom: 8,
|
|
318
|
+
paddingHorizontal: 8,
|
|
319
|
+
backgroundColor: "#FFFFFF",
|
|
320
|
+
},
|
|
321
|
+
checkbox: {
|
|
322
|
+
width: 24,
|
|
323
|
+
height: 24,
|
|
324
|
+
borderRadius: 6,
|
|
325
|
+
borderWidth: 2,
|
|
326
|
+
borderColor: "#D1D5DB",
|
|
327
|
+
alignItems: "center",
|
|
328
|
+
justifyContent: "center",
|
|
329
|
+
marginRight: 10,
|
|
330
|
+
},
|
|
331
|
+
checkboxCompleted: {
|
|
332
|
+
backgroundColor: "#10B981",
|
|
333
|
+
borderColor: "#10B981",
|
|
334
|
+
},
|
|
335
|
+
todoText: {
|
|
336
|
+
flex: 1,
|
|
337
|
+
color: "#1F2937",
|
|
338
|
+
fontSize: 16,
|
|
339
|
+
},
|
|
340
|
+
todoTextCompleted: {
|
|
341
|
+
color: "#9CA3AF",
|
|
342
|
+
textDecorationLine: "line-through",
|
|
343
|
+
},
|
|
344
|
+
deleteButton: {
|
|
345
|
+
width: 40,
|
|
346
|
+
height: 40,
|
|
347
|
+
borderRadius: 10,
|
|
348
|
+
alignItems: "center",
|
|
349
|
+
justifyContent: "center",
|
|
350
|
+
},
|
|
351
|
+
setupContainer: {
|
|
352
|
+
flex: 1,
|
|
353
|
+
justifyContent: "center",
|
|
354
|
+
paddingHorizontal: 24,
|
|
355
|
+
},
|
|
356
|
+
setupTitle: {
|
|
357
|
+
fontSize: 24,
|
|
358
|
+
fontWeight: "700",
|
|
359
|
+
color: "#111827",
|
|
360
|
+
marginBottom: 12,
|
|
361
|
+
textAlign: "center",
|
|
362
|
+
},
|
|
363
|
+
setupText: {
|
|
364
|
+
fontSize: 15,
|
|
365
|
+
color: "#4B5563",
|
|
366
|
+
textAlign: "center",
|
|
367
|
+
lineHeight: 22,
|
|
368
|
+
},
|
|
369
|
+
setupHint: {
|
|
370
|
+
marginTop: 16,
|
|
371
|
+
fontSize: 13,
|
|
372
|
+
color: "#6B7280",
|
|
373
|
+
textAlign: "center",
|
|
374
|
+
lineHeight: 20,
|
|
375
|
+
},
|
|
376
|
+
loadingContainer: {
|
|
377
|
+
flex: 1,
|
|
378
|
+
justifyContent: "center",
|
|
379
|
+
alignItems: "center",
|
|
380
|
+
paddingHorizontal: 24,
|
|
381
|
+
},
|
|
382
|
+
loadingText: {
|
|
383
|
+
color: "#4B5563",
|
|
384
|
+
fontSize: 16,
|
|
385
|
+
},
|
|
386
|
+
errorText: {
|
|
387
|
+
color: "#DC2626",
|
|
388
|
+
fontSize: 15,
|
|
389
|
+
textAlign: "center",
|
|
390
|
+
},
|
|
391
|
+
emptyState: {
|
|
392
|
+
marginTop: 48,
|
|
393
|
+
alignItems: "center",
|
|
394
|
+
},
|
|
395
|
+
emptyTitle: {
|
|
396
|
+
fontSize: 18,
|
|
397
|
+
fontWeight: "600",
|
|
398
|
+
color: "#1F2937",
|
|
399
|
+
},
|
|
400
|
+
emptySubtitle: {
|
|
401
|
+
marginTop: 8,
|
|
402
|
+
color: "#6B7280",
|
|
403
|
+
fontSize: 14,
|
|
404
|
+
textAlign: "center",
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
`;
|
|
408
|
+
}
|