create-rn-folder-structure 1.0.2 → 1.0.3
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/generators/components-advanced.js +221 -0
- package/generators/components-basic.js +243 -0
- package/generators/components-intermediate.js +290 -0
- package/generators/components-upload.js +181 -0
- package/generators/components.js +659 -0
- package/generators/constants.js +54 -0
- package/generators/features.js +51 -0
- package/generators/helpers.js +195 -0
- package/generators/hooks.js +239 -0
- package/generators/navigation.js +138 -0
- package/generators/readme.js +51 -0
- package/generators/shared.js +56 -0
- package/generators/storage.js +175 -0
- package/generators/store.js +181 -0
- package/generators/utils.js +202 -0
- package/package.json +3 -2
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
function write(root, filePath, content) {
|
|
5
|
+
const fullPath = path.join(root, filePath);
|
|
6
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
7
|
+
fs.writeFileSync(fullPath, content);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = function generateIntermediateComponents(projectRoot) {
|
|
11
|
+
console.log("🧩 Creating Intermediate UI Components...");
|
|
12
|
+
|
|
13
|
+
const base = "src/shared/components";
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------
|
|
16
|
+
// Modal Component
|
|
17
|
+
// ---------------------------------------------------------
|
|
18
|
+
write(
|
|
19
|
+
projectRoot,
|
|
20
|
+
`${base}/AppModal.tsx`,
|
|
21
|
+
`
|
|
22
|
+
import React from "react";
|
|
23
|
+
import { Modal, View, StyleSheet, TouchableOpacity, Text } from "react-native";
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
visible: boolean;
|
|
27
|
+
onClose: () => void;
|
|
28
|
+
title?: string;
|
|
29
|
+
children?: any;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function AppModal({ visible, onClose, title, children }: Props) {
|
|
33
|
+
return (
|
|
34
|
+
<Modal visible={visible} transparent animationType="fade">
|
|
35
|
+
<View style={styles.overlay}>
|
|
36
|
+
<View style={styles.box}>
|
|
37
|
+
{title && <Text style={styles.title}>{title}</Text>}
|
|
38
|
+
{children}
|
|
39
|
+
<TouchableOpacity style={styles.closeBtn} onPress={onClose}>
|
|
40
|
+
<Text style={styles.closeText}>Close</Text>
|
|
41
|
+
</TouchableOpacity>
|
|
42
|
+
</View>
|
|
43
|
+
</View>
|
|
44
|
+
</Modal>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const styles = StyleSheet.create({
|
|
49
|
+
overlay: {
|
|
50
|
+
flex: 1,
|
|
51
|
+
backgroundColor: "rgba(0,0,0,0.4)",
|
|
52
|
+
justifyContent: "center",
|
|
53
|
+
alignItems: "center",
|
|
54
|
+
},
|
|
55
|
+
box: {
|
|
56
|
+
backgroundColor: "#fff",
|
|
57
|
+
width: "85%",
|
|
58
|
+
padding: 20,
|
|
59
|
+
borderRadius: 10,
|
|
60
|
+
},
|
|
61
|
+
title: {
|
|
62
|
+
fontSize: 18,
|
|
63
|
+
fontWeight: "600",
|
|
64
|
+
marginBottom: 10,
|
|
65
|
+
},
|
|
66
|
+
closeBtn: {
|
|
67
|
+
marginTop: 10,
|
|
68
|
+
backgroundColor: "#007AFF",
|
|
69
|
+
paddingVertical: 10,
|
|
70
|
+
borderRadius: 6,
|
|
71
|
+
},
|
|
72
|
+
closeText: {
|
|
73
|
+
textAlign: "center",
|
|
74
|
+
color: "#fff",
|
|
75
|
+
fontWeight: "600",
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
`
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------
|
|
82
|
+
// Dropdown (simple custom)
|
|
83
|
+
// ---------------------------------------------------------
|
|
84
|
+
write(
|
|
85
|
+
projectRoot,
|
|
86
|
+
`${base}/Dropdown.tsx`,
|
|
87
|
+
`
|
|
88
|
+
import React, { useState } from "react";
|
|
89
|
+
import {
|
|
90
|
+
View,
|
|
91
|
+
Text,
|
|
92
|
+
TouchableOpacity,
|
|
93
|
+
StyleSheet,
|
|
94
|
+
FlatList,
|
|
95
|
+
} from "react-native";
|
|
96
|
+
|
|
97
|
+
interface Item {
|
|
98
|
+
label: string;
|
|
99
|
+
value: any;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface Props {
|
|
103
|
+
data: Item[];
|
|
104
|
+
onSelect: (value: any) => void;
|
|
105
|
+
placeholder?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default function Dropdown({ data, onSelect, placeholder }: Props) {
|
|
109
|
+
const [open, setOpen] = useState(false);
|
|
110
|
+
const [selected, setSelected] = useState("");
|
|
111
|
+
|
|
112
|
+
const handleSelect = (item: Item) => {
|
|
113
|
+
setSelected(item.label);
|
|
114
|
+
onSelect(item.value);
|
|
115
|
+
setOpen(false);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<View>
|
|
120
|
+
<TouchableOpacity style={styles.box} onPress={() => setOpen(!open)}>
|
|
121
|
+
<Text style={styles.text}>
|
|
122
|
+
{selected || placeholder || "Select"}
|
|
123
|
+
</Text>
|
|
124
|
+
</TouchableOpacity>
|
|
125
|
+
|
|
126
|
+
{open && (
|
|
127
|
+
<View style={styles.dropdown}>
|
|
128
|
+
<FlatList
|
|
129
|
+
data={data}
|
|
130
|
+
keyExtractor={(item) => item.value.toString()}
|
|
131
|
+
renderItem={({ item }) => (
|
|
132
|
+
<TouchableOpacity
|
|
133
|
+
style={styles.item}
|
|
134
|
+
onPress={() => handleSelect(item)}
|
|
135
|
+
>
|
|
136
|
+
<Text>{item.label}</Text>
|
|
137
|
+
</TouchableOpacity>
|
|
138
|
+
)}
|
|
139
|
+
/>
|
|
140
|
+
</View>
|
|
141
|
+
)}
|
|
142
|
+
</View>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const styles = StyleSheet.create({
|
|
147
|
+
box: {
|
|
148
|
+
borderWidth: 1,
|
|
149
|
+
borderColor: "#ccc",
|
|
150
|
+
padding: 12,
|
|
151
|
+
borderRadius: 8,
|
|
152
|
+
marginVertical: 6,
|
|
153
|
+
},
|
|
154
|
+
text: { fontSize: 16 },
|
|
155
|
+
dropdown: {
|
|
156
|
+
backgroundColor: "#fff",
|
|
157
|
+
borderWidth: 1,
|
|
158
|
+
borderColor: "#ccc",
|
|
159
|
+
marginTop: 4,
|
|
160
|
+
borderRadius: 8,
|
|
161
|
+
maxHeight: 160,
|
|
162
|
+
},
|
|
163
|
+
item: {
|
|
164
|
+
padding: 12,
|
|
165
|
+
borderBottomWidth: 1,
|
|
166
|
+
borderBottomColor: "#eee",
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
`
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------
|
|
173
|
+
// SearchBar
|
|
174
|
+
// ---------------------------------------------------------
|
|
175
|
+
write(
|
|
176
|
+
projectRoot,
|
|
177
|
+
`${base}/SearchBar.tsx`,
|
|
178
|
+
`
|
|
179
|
+
import React from "react";
|
|
180
|
+
import { View, TextInput, StyleSheet } from "react-native";
|
|
181
|
+
|
|
182
|
+
export default function SearchBar({ value, onChangeText }: any) {
|
|
183
|
+
return (
|
|
184
|
+
<View style={styles.box}>
|
|
185
|
+
<TextInput
|
|
186
|
+
value={value}
|
|
187
|
+
onChangeText={onChangeText}
|
|
188
|
+
placeholder="Search..."
|
|
189
|
+
style={styles.input}
|
|
190
|
+
/>
|
|
191
|
+
</View>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const styles = StyleSheet.create({
|
|
196
|
+
box: {
|
|
197
|
+
borderWidth: 1,
|
|
198
|
+
borderColor: "#ccc",
|
|
199
|
+
paddingHorizontal: 12,
|
|
200
|
+
paddingVertical: 6,
|
|
201
|
+
borderRadius: 8,
|
|
202
|
+
marginVertical: 6,
|
|
203
|
+
},
|
|
204
|
+
input: {
|
|
205
|
+
fontSize: 16,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
`
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// ---------------------------------------------------------
|
|
212
|
+
// FlatListCard
|
|
213
|
+
// ---------------------------------------------------------
|
|
214
|
+
write(
|
|
215
|
+
projectRoot,
|
|
216
|
+
`${base}/FlatListCard.tsx`,
|
|
217
|
+
`
|
|
218
|
+
import React from "react";
|
|
219
|
+
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
|
|
220
|
+
|
|
221
|
+
interface Props {
|
|
222
|
+
title: string;
|
|
223
|
+
subtitle?: string;
|
|
224
|
+
onPress?: () => void;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export default function FlatListCard({ title, subtitle, onPress }: Props) {
|
|
228
|
+
return (
|
|
229
|
+
<TouchableOpacity style={styles.card} onPress={onPress}>
|
|
230
|
+
<View>
|
|
231
|
+
<Text style={styles.title}>{title}</Text>
|
|
232
|
+
{subtitle && <Text style={styles.sub}>{subtitle}</Text>}
|
|
233
|
+
</View>
|
|
234
|
+
</TouchableOpacity>
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const styles = StyleSheet.create({
|
|
239
|
+
card: {
|
|
240
|
+
padding: 15,
|
|
241
|
+
backgroundColor: "#fff",
|
|
242
|
+
marginVertical: 6,
|
|
243
|
+
borderRadius: 8,
|
|
244
|
+
elevation: 2,
|
|
245
|
+
},
|
|
246
|
+
title: { fontSize: 16, fontWeight: "600" },
|
|
247
|
+
sub: { color: "#666", marginTop: 4 },
|
|
248
|
+
});
|
|
249
|
+
`
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// ---------------------------------------------------------
|
|
253
|
+
// AlertMessage
|
|
254
|
+
// ---------------------------------------------------------
|
|
255
|
+
write(
|
|
256
|
+
projectRoot,
|
|
257
|
+
`${base}/AlertMessage.tsx`,
|
|
258
|
+
`
|
|
259
|
+
import React from "react";
|
|
260
|
+
import { View, Text, StyleSheet } from "react-native";
|
|
261
|
+
|
|
262
|
+
interface Props {
|
|
263
|
+
type?: "success" | "error" | "info";
|
|
264
|
+
message: string;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export default function AlertMessage({ type = "info", message }: Props) {
|
|
268
|
+
return (
|
|
269
|
+
<View style={[styles.box, styles[type]]}>
|
|
270
|
+
<Text style={styles.text}>{message}</Text>
|
|
271
|
+
</View>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const styles = StyleSheet.create({
|
|
276
|
+
box: {
|
|
277
|
+
padding: 12,
|
|
278
|
+
borderRadius: 8,
|
|
279
|
+
marginVertical: 6,
|
|
280
|
+
},
|
|
281
|
+
text: { color: "#fff", fontWeight: "600" },
|
|
282
|
+
success: { backgroundColor: "green" },
|
|
283
|
+
error: { backgroundColor: "red" },
|
|
284
|
+
info: { backgroundColor: "blue" },
|
|
285
|
+
});
|
|
286
|
+
`
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
console.log("✅ Intermediate Components Created!");
|
|
290
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
function write(root, filePath, content) {
|
|
5
|
+
const fullPath = path.join(root, filePath);
|
|
6
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
7
|
+
fs.writeFileSync(fullPath, content);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = function generateUploadComponents(projectRoot) {
|
|
11
|
+
console.log("📤 Creating Upload Components...");
|
|
12
|
+
|
|
13
|
+
const base = "src/shared/components";
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------
|
|
16
|
+
// 📄 File Upload
|
|
17
|
+
// ---------------------------------------------------------
|
|
18
|
+
write(
|
|
19
|
+
projectRoot,
|
|
20
|
+
`${base}/FileUpload.tsx`,
|
|
21
|
+
`
|
|
22
|
+
import React, { useState } from "react";
|
|
23
|
+
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
|
|
24
|
+
import DocumentPicker from "react-native-document-picker";
|
|
25
|
+
|
|
26
|
+
export default function FileUpload({ onSelect }: { onSelect: (file: any) => void }) {
|
|
27
|
+
const [fileName, setFileName] = useState("");
|
|
28
|
+
|
|
29
|
+
const pickFile = async () => {
|
|
30
|
+
try {
|
|
31
|
+
const res = await DocumentPicker.pickSingle({
|
|
32
|
+
type: [DocumentPicker.types.allFiles],
|
|
33
|
+
});
|
|
34
|
+
setFileName(res.name);
|
|
35
|
+
onSelect(res);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (!DocumentPicker.isCancel(err)) console.log(err);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<View>
|
|
43
|
+
<TouchableOpacity style={styles.btn} onPress={pickFile}>
|
|
44
|
+
<Text style={styles.btnText}>Select File</Text>
|
|
45
|
+
</TouchableOpacity>
|
|
46
|
+
{fileName ? <Text style={styles.label}>📄 {fileName}</Text> : null}
|
|
47
|
+
</View>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const styles = StyleSheet.create({
|
|
52
|
+
btn: {
|
|
53
|
+
backgroundColor: "#007AFF",
|
|
54
|
+
padding: 10,
|
|
55
|
+
borderRadius: 6,
|
|
56
|
+
alignItems: "center",
|
|
57
|
+
},
|
|
58
|
+
btnText: { color: "#fff", fontWeight: "600" },
|
|
59
|
+
label: { marginTop: 8, fontSize: 14 },
|
|
60
|
+
});
|
|
61
|
+
`
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------
|
|
65
|
+
// 🖼 Image Upload
|
|
66
|
+
// ---------------------------------------------------------
|
|
67
|
+
write(
|
|
68
|
+
projectRoot,
|
|
69
|
+
`${base}/ImageUpload.tsx`,
|
|
70
|
+
`
|
|
71
|
+
import React, { useState } from "react";
|
|
72
|
+
import { View, Text, Image, TouchableOpacity, StyleSheet } from "react-native";
|
|
73
|
+
import { launchImageLibrary } from "react-native-image-picker";
|
|
74
|
+
|
|
75
|
+
export default function ImageUpload({
|
|
76
|
+
onSelect,
|
|
77
|
+
}: {
|
|
78
|
+
onSelect: (file: any) => void;
|
|
79
|
+
}) {
|
|
80
|
+
const [image, setImage] = useState("");
|
|
81
|
+
|
|
82
|
+
const pickImage = () => {
|
|
83
|
+
launchImageLibrary({ mediaType: "photo" }, (res) => {
|
|
84
|
+
if (res.assets && res.assets.length > 0) {
|
|
85
|
+
const asset = res.assets[0];
|
|
86
|
+
setImage(asset.uri || "");
|
|
87
|
+
onSelect(asset);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<View>
|
|
94
|
+
<TouchableOpacity style={styles.btn} onPress={pickImage}>
|
|
95
|
+
<Text style={styles.btnText}>Select Image</Text>
|
|
96
|
+
</TouchableOpacity>
|
|
97
|
+
|
|
98
|
+
{image ? <Image source={{ uri: image }} style={styles.preview} /> : null}
|
|
99
|
+
</View>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const styles = StyleSheet.create({
|
|
104
|
+
btn: {
|
|
105
|
+
backgroundColor: "#007AFF",
|
|
106
|
+
padding: 10,
|
|
107
|
+
borderRadius: 6,
|
|
108
|
+
alignItems: "center",
|
|
109
|
+
},
|
|
110
|
+
btnText: { color: "#fff", fontWeight: "600" },
|
|
111
|
+
preview: {
|
|
112
|
+
width: 120,
|
|
113
|
+
height: 120,
|
|
114
|
+
borderRadius: 8,
|
|
115
|
+
marginTop: 10,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
`
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------
|
|
122
|
+
// 🎥 Video Upload
|
|
123
|
+
// ---------------------------------------------------------
|
|
124
|
+
write(
|
|
125
|
+
projectRoot,
|
|
126
|
+
`${base}/VideoUpload.tsx`,
|
|
127
|
+
`
|
|
128
|
+
import React, { useState } from "react";
|
|
129
|
+
import { View, Text, TouchableOpacity, StyleSheet, Image } from "react-native";
|
|
130
|
+
import { launchImageLibrary } from "react-native-image-picker";
|
|
131
|
+
|
|
132
|
+
export default function VideoUpload({
|
|
133
|
+
onSelect,
|
|
134
|
+
}: {
|
|
135
|
+
onSelect: (file: any) => void;
|
|
136
|
+
}) {
|
|
137
|
+
const [thumbnail, setThumbnail] = useState("");
|
|
138
|
+
|
|
139
|
+
const pickVideo = () => {
|
|
140
|
+
launchImageLibrary({ mediaType: "video" }, (res) => {
|
|
141
|
+
if (res.assets && res.assets.length > 0) {
|
|
142
|
+
const asset = res.assets[0];
|
|
143
|
+
setThumbnail(asset.uri || "");
|
|
144
|
+
onSelect(asset);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<View>
|
|
151
|
+
<TouchableOpacity style={styles.btn} onPress={pickVideo}>
|
|
152
|
+
<Text style={styles.btnText}>Select Video</Text>
|
|
153
|
+
</TouchableOpacity>
|
|
154
|
+
|
|
155
|
+
{thumbnail ? (
|
|
156
|
+
<Image source={{ uri: thumbnail }} style={styles.thumbnail} />
|
|
157
|
+
) : null}
|
|
158
|
+
</View>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const styles = StyleSheet.create({
|
|
163
|
+
btn: {
|
|
164
|
+
backgroundColor: "#007AFF",
|
|
165
|
+
padding: 10,
|
|
166
|
+
borderRadius: 6,
|
|
167
|
+
alignItems: "center",
|
|
168
|
+
},
|
|
169
|
+
btnText: { color: "#fff", fontWeight: "600" },
|
|
170
|
+
thumbnail: {
|
|
171
|
+
width: 140,
|
|
172
|
+
height: 100,
|
|
173
|
+
borderRadius: 8,
|
|
174
|
+
marginTop: 10,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
`
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
console.log("✅ Upload Components Created!");
|
|
181
|
+
};
|